75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi/*
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * CDDL HEADER START
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The contents of this file are subject to the terms of the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Common Development and Distribution License, Version 1.0 only
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * (the "License"). You may not use this file except in compliance
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * with the License.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * or http://forgerock.org/license/CDDLv1.0.html.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * See the License for the specific language governing permissions
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * and limitations under the License.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * When distributing Covered Code, include this CDDL HEADER in each
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * file and include the License file at legal-notices/CDDLv1_0.txt.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If applicable, add the following below this CDDL HEADER, with the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * fields enclosed by brackets "[]" replaced with your own identifying
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * information:
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Portions Copyright [yyyy] [name of copyright owner]
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * CDDL HEADER END
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Copyright 2006-2009 Sun Microsystems, Inc.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Portions Copyright 2013-2015 ForgeRock AS.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponipackage org.opends.server.util;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport static java.util.Collections.*;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport static org.opends.messages.BackendMessages.*;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport static org.opends.messages.UtilityMessages.*;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport static org.opends.server.util.ServerConstants.*;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport static org.opends.server.util.StaticUtils.*;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.BufferedReader;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.Closeable;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.File;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.FileFilter;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.FileInputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.FileNotFoundException;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.FileOutputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.IOException;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.InputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.InputStreamReader;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.OutputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.OutputStreamWriter;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.io.Writer;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.nio.file.Files;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.nio.file.Path;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.nio.file.Paths;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.security.MessageDigest;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.ArrayList;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.Arrays;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.Collections;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.Date;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.HashMap;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.HashSet;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.List;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.ListIterator;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.Map;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.Set;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.regex.Pattern;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.zip.Deflater;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.zip.ZipEntry;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.zip.ZipInputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport java.util.zip.ZipOutputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport javax.crypto.Mac;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.forgerock.i18n.LocalizableMessage;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.forgerock.i18n.slf4j.LocalizedLogger;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.forgerock.opendj.config.server.ConfigException;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.forgerock.opendj.ldap.ResultCode;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.forgerock.util.Pair;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.api.Backupable;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.core.DirectoryServer;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.BackupConfig;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.BackupDirectory;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.BackupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.CryptoManager;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.CryptoManagerException;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.DirectoryException;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponiimport org.opends.server.types.RestoreConfig;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi/**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * A backup manager for any entity that is backupable (backend, storage).
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @see {@link Backupable}
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponipublic class BackupManager
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi{
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The common prefix for archive files.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final String BACKUP_BASE_FILENAME = "backup-";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The name of the property that holds the name of the latest log file
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * at the time the backup was created.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final String PROPERTY_LAST_LOGFILE_NAME = "last_logfile_name";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The name of the property that holds the size of the latest log file
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * at the time the backup was created.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final String PROPERTY_LAST_LOGFILE_SIZE = "last_logfile_size";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The name of the entry in an incremental backup archive file
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * containing a list of log files that are unchanged since the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * previous backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final String ZIPENTRY_UNCHANGED_LOGFILES = "unchanged.txt";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The name of a dummy entry in the backup archive file that will act
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * as a placeholder in case a backup is done on an empty backend.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final String ZIPENTRY_EMPTY_PLACEHOLDER = "empty.placeholder";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The backend ID.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final String backendID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Construct a backup manager for a backend.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backendID
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The ID of the backend instance for which a backup manager is
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * required.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public BackupManager(String backendID)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backendID = backendID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** A cryptographic engine to use for backup creation or restore. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static abstract class CryptoEngine
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final CryptoManager cryptoManager;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final boolean shouldEncrypt;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Creates a crypto engine for archive creation. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi static CryptoEngine forCreation(BackupConfig backupConfig, NewBackupParams backupParams)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupConfig.hashData())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupConfig.signHash())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new MacCryptoEngine(backupConfig, backupParams);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new DigestCryptoEngine(backupConfig, backupParams);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new NoHashCryptoEngine(backupConfig.encryptData());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Creates a crypto engine for archive restore. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi static CryptoEngine forRestore(BackupInfo backupInfo)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean hasSignedHash = backupInfo.getSignedHash() != null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean hasHashData = hasSignedHash || backupInfo.getUnsignedHash() != null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (hasHashData)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (hasSignedHash)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new MacCryptoEngine(backupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new DigestCryptoEngine(backupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new NoHashCryptoEngine(backupInfo.isEncrypted());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi CryptoEngine(boolean shouldEncrypt)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoManager = DirectoryServer.getCryptoManager();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.shouldEncrypt = shouldEncrypt;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Indicates if data is encrypted. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final boolean shouldEncrypt() {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return shouldEncrypt;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Indicates if hashed data is signed. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean hasSignedHash() {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return false;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Update the hash with the provided string. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi abstract void updateHashWith(String s);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Update the hash with the provided buffer. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi abstract void updateHashWith(byte[] buffer, int offset, int len);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Generates the hash bytes. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi abstract byte[] generateBytes();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Returns the error message to use in case of check failure. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi abstract LocalizableMessage getErrorMessageForCheck(String backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Check that generated hash is equal to the provided hash. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final void check(byte[] hash, String backupID) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] bytes = generateBytes();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (bytes != null && !Arrays.equals(bytes, hash))
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = getErrorMessageForCheck(backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Wraps an output stream in a cipher output stream if encryption is required. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final OutputStream encryptOutput(OutputStream output) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!shouldEncrypt())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return output;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return cryptoManager.getCipherOutputStream(output);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (CryptoManagerException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(output);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_CANNOT_GET_CIPHER.get(stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Wraps an input stream in a cipher input stream if encryption is required. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final InputStream encryptInput(InputStream inputStream) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!shouldEncrypt)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return inputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return cryptoManager.getCipherInputStream(inputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (CryptoManagerException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(inputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_CANNOT_GET_CIPHER.get(stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Represents the cryptographic engine with no hash used for a backup. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class NoHashCryptoEngine extends CryptoEngine
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi NoHashCryptoEngine(boolean shouldEncrypt)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi super(shouldEncrypt);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void updateHashWith(String s)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // nothing to do
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void updateHashWith(byte[] buffer, int offset, int len)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // nothing to do
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] generateBytes()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage getErrorMessageForCheck(String backupID)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // check never fails because bytes are always null
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Represents the cryptographic engine with signed hash.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class MacCryptoEngine extends CryptoEngine
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private Mac mac;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Constructor for backup creation. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private MacCryptoEngine(BackupConfig backupConfig, NewBackupParams backupParams) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi super(backupConfig.encryptData());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String macKeyID = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi macKeyID = cryptoManager.getMacEngineKeyEntryID();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupParams.putProperty(BACKUP_PROPERTY_MAC_KEY_ID, macKeyID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (CryptoManagerException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_CANNOT_GET_MAC_KEY_ID.get(backupParams.backupID,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi retrieveMacEngine(macKeyID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Constructor for backup restore. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private MacCryptoEngine(BackupInfo backupInfo) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi super(backupInfo.isEncrypted());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi HashMap<String,String> backupProperties = backupInfo.getBackupProperties();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String macKeyID = backupProperties.get(BACKUP_PROPERTY_MAC_KEY_ID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi retrieveMacEngine(macKeyID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void retrieveMacEngine(String macKeyID) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi mac = cryptoManager.getMacEngine(macKeyID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_CANNOT_GET_MAC.get(macKeyID, stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** {@inheritDoc} */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void updateHashWith(String s)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi mac.update(getBytes(s));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** {@inheritDoc} */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void updateHashWith(byte[] buffer, int offset, int len)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi mac.update(buffer, offset, len);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] generateBytes()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return mac.doFinal();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean hasSignedHash()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return true;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage getErrorMessageForCheck(String backupID)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return ERR_BACKUP_SIGNED_HASH_ERROR.get(backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public String toString()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return "MacCryptoEngine [mac=" + mac + "]";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Represents the cryptographic engine with unsigned hash used for a backup. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class DigestCryptoEngine extends CryptoEngine
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final MessageDigest digest;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Constructor for backup creation. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private DigestCryptoEngine(BackupConfig backupConfig, NewBackupParams backupParams) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi super(backupConfig.encryptData());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String digestAlgorithm = cryptoManager.getPreferredMessageDigestAlgorithm();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupParams.putProperty(BACKUP_PROPERTY_DIGEST_ALGORITHM, digestAlgorithm);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi digest = retrieveMessageDigest(digestAlgorithm);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Constructor for backup restore. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private DigestCryptoEngine(BackupInfo backupInfo) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi super(backupInfo.isEncrypted());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi HashMap<String, String> backupProperties = backupInfo.getBackupProperties();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String digestAlgorithm = backupProperties.get(BACKUP_PROPERTY_DIGEST_ALGORITHM);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi digest = retrieveMessageDigest(digestAlgorithm);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private MessageDigest retrieveMessageDigest(String digestAlgorithm) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return cryptoManager.getMessageDigest(digestAlgorithm);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message =
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_GET_DIGEST.get(digestAlgorithm, stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** {@inheritDoc} */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public void updateHashWith(String s)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi digest.update(getBytes(s));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** {@inheritDoc} */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public void updateHashWith(byte[] buffer, int offset, int len)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi digest.update(buffer, offset, len);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** {@inheritDoc} */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public byte[] generateBytes()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return digest.digest();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** {@inheritDoc} */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage getErrorMessageForCheck(String backupID)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return ERR_BACKUP_UNSIGNED_HASH_ERROR.get(backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public String toString()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return "DigestCryptoEngine [digest=" + digest + "]";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Contains all parameters for creation of a new backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class NewBackupParams
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final String backupID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final BackupDirectory backupDir;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final HashMap<String,String> backupProperties;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final boolean shouldCompress;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final boolean isIncremental;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final String incrementalBaseID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final BackupInfo baseBackupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi NewBackupParams(BackupConfig backupConfig) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupID = backupConfig.getBackupID();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupDir = backupConfig.getBackupDirectory();
a89f7014aeb71dba5c94404dfea7eb89e7eeee74Jean-Noel Rouvignac backupProperties = new HashMap<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi shouldCompress = backupConfig.compressData();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi incrementalBaseID = retrieveIncrementalBaseID(backupConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi isIncremental = incrementalBaseID != null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi baseBackupInfo = isIncremental ? getBackupInfo(backupDir, incrementalBaseID) : null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private String retrieveIncrementalBaseID(BackupConfig backupConfig)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String id = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupConfig.isIncremental())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupConfig.getIncrementalBaseID() == null && backupDir.getLatestBackup() != null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // The default is to use the latest backup as base.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi id = backupDir.getLatestBackup().getBackupID();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi id = backupConfig.getIncrementalBaseID();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (id == null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // No incremental backup ID: log a message informing that a backup
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // could not be found and that a normal backup will be done.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.warn(WARN_BACKUPDB_INCREMENTAL_NOT_FOUND_DOING_NORMAL, backupDir.getPath());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return id;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void putProperty(String name, String value) {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupProperties.put(name, value);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public String toString()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return "BackupCreationParams [backupID=" + backupID + ", backupDir=" + backupDir.getPath() + "]";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Represents a new backup archive. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class NewBackupArchive {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final String archiveFilename;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private String latestFileName;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private long latestFileSize;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final HashSet<String> dependencies;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final String backendID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final NewBackupParams newBackupParams;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final CryptoEngine cryptoEngine;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi NewBackupArchive(String backendID, NewBackupParams backupParams, CryptoEngine crypt)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backendID = backendID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.newBackupParams = backupParams;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.cryptoEngine = crypt;
a89f7014aeb71dba5c94404dfea7eb89e7eeee74Jean-Noel Rouvignac dependencies = new HashSet<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupParams.isIncremental)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi HashMap<String,String> properties = backupParams.baseBackupInfo.getBackupProperties();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi latestFileName = properties.get(PROPERTY_LAST_LOGFILE_NAME);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi latestFileSize = Long.parseLong(properties.get(PROPERTY_LAST_LOGFILE_SIZE));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveFilename = BACKUP_BASE_FILENAME + backendID + "-" + backupParams.backupID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String getArchiveFilename()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return archiveFilename;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String getBackendID()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return backendID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String getBackupID()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return newBackupParams.backupID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String getBackupPath() {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return newBackupParams.backupDir.getPath();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void addBaseBackupAsDependency() {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi dependencies.add(newBackupParams.baseBackupInfo.getBackupID());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void updateBackupDirectory() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupInfo backupInfo = createDescriptorForBackup();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.backupDir.addBackup(backupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.backupDir.writeBackupDirectoryDescriptor();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_UPDATE_BACKUP_DESCRIPTOR.get(
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.backupDir.getDescriptorPath(), stackTraceToSingleLineString(e)),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Create a descriptor for the backup. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private BackupInfo createDescriptorForBackup()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] bytes = cryptoEngine.generateBytes();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] digestBytes = cryptoEngine.hasSignedHash() ? null : bytes;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] macBytes = cryptoEngine.hasSignedHash() ? bytes : null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.putProperty(PROPERTY_LAST_LOGFILE_NAME, latestFileName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.putProperty(PROPERTY_LAST_LOGFILE_SIZE, String.valueOf(latestFileSize));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new BackupInfo(
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.backupDir, newBackupParams.backupID, new Date(), newBackupParams.isIncremental,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newBackupParams.shouldCompress, cryptoEngine.shouldEncrypt(), digestBytes, macBytes,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi dependencies, newBackupParams.backupProperties);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public String toString()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return "NewArchive [archive file=" + archiveFilename + ", latestFileName=" + latestFileName
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi + ", backendID=" + backendID + "]";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Represents an existing backup archive. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class ExistingBackupArchive {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final String backupID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final BackupDirectory backupDir;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final BackupInfo backupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final CryptoEngine cryptoEngine;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final File archiveFile;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ExistingBackupArchive(String backupID, BackupDirectory backupDir) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backupID = backupID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backupDir = backupDir;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backupInfo = BackupManager.getBackupInfo(backupDir, backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.cryptoEngine = CryptoEngine.forRestore(backupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.archiveFile = BackupManager.retrieveArchiveFile(backupInfo, backupDir.getPath());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi File getArchiveFile()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return archiveFile;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupInfo getBackupInfo() {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return backupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String getBackupID()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return backupID;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi CryptoEngine getCryptoEngine()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return cryptoEngine;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Obtains a list of the dependencies of this backup in order from
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * the oldest (the full backup), to the most recent.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return A list of dependent backups.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException If a Directory Server error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi List<BackupInfo> getBackupDependencies() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
a89f7014aeb71dba5c94404dfea7eb89e7eeee74Jean-Noel Rouvignac List<BackupInfo> dependencies = new ArrayList<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupInfo currentBackupInfo = backupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (currentBackupInfo != null && !currentBackupInfo.getDependencies().isEmpty())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String backupID = currentBackupInfo.getDependencies().iterator().next();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi currentBackupInfo = backupDir.getBackupInfo(backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (currentBackupInfo != null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi dependencies.add(currentBackupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Collections.reverse(dependencies);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return dependencies;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean hasDependencies()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return !backupInfo.getDependencies().isEmpty();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Removes the archive from file system. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean removeArchive() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupDir.removeBackup(backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupDir.writeBackupDirectoryDescriptor();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (ConfigException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), e.getMessageObject());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_CANNOT_UPDATE_BACKUP_DESCRIPTOR.get(
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupDir.getDescriptorPath(), stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return archiveFile.delete();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Represents a writer of a backup archive. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class BackupArchiveWriter implements Closeable {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final ZipOutputStream zipOutputStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final NewBackupArchive archive;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final CryptoEngine cryptoEngine;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupArchiveWriter(NewBackupArchive archive) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.archive = archive;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.cryptoEngine = archive.cryptoEngine;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.zipOutputStream = open(archive.getBackupPath(), archive.getArchiveFilename());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public void close() throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(zipOutputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Writes the provided file to a new entry in the archive.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param file
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The file to be written.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param cryptoMethod
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The cryptographic method for the written data.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupConfig
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The configuration, used to know if operation is cancelled.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return The number of bytes written from the file.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws FileNotFoundException If the file to be archived does not exist.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws IOException If an I/O error occurs while archiving the file.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi long writeFile(Path file, String relativePath, CryptoEngine cryptoMethod, BackupConfig backupConfig)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws IOException, FileNotFoundException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipEntry zipEntry = new ZipEntry(relativePath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipOutputStream.putNextEntry(zipEntry);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoMethod.updateHashWith(relativePath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi InputStream inputStream = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi long totalBytesRead = 0;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi inputStream = new FileInputStream(file.toFile());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] buffer = new byte[8192];
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int bytesRead = inputStream.read(buffer);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (bytesRead > 0 && !backupConfig.isCancelled())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoMethod.updateHashWith(buffer, 0, bytesRead);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipOutputStream.write(buffer, 0, bytesRead);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi totalBytesRead += bytesRead;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi bytesRead = inputStream.read(buffer);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi finally {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(inputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipOutputStream.closeEntry();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.info(NOTE_BACKUP_ARCHIVED_FILE, zipEntry.getName());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return totalBytesRead;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Write a list of strings to an entry in the archive.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param stringList
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * A list of strings to be written. The strings must not
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * contain newlines.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param fileName
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The name of the zip entry to be written.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param cryptoMethod
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The cryptographic method for the written data.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If an I/O error occurs while writing the archive entry.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void writeStrings(List<String> stringList, String fileName, CryptoEngine cryptoMethod)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipEntry zipEntry = new ZipEntry(fileName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipOutputStream.putNextEntry(zipEntry);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoMethod.updateHashWith(fileName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Writer writer = new OutputStreamWriter(zipOutputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi for (String s : stringList)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoMethod.updateHashWith(s);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi writer.write(s);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi writer.write(EOL);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi writer.flush();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipOutputStream.closeEntry();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Writes a empty placeholder entry into the archive. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void writeEmptyPlaceHolder() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipEntry emptyPlaceholder = new ZipEntry(ZIPENTRY_EMPTY_PLACEHOLDER);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipOutputStream.putNextEntry(emptyPlaceholder);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_WRITE_ARCHIVE_FILE.get(ZIPENTRY_EMPTY_PLACEHOLDER, archive.getBackupID(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi stackTraceToSingleLineString(e)),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Writes the files that are unchanged from the base backup (for an
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * incremental backup only).
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The unchanged files names are listed in the "unchanged.txt" file, which
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * is put in the archive.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void writeUnchangedFiles(Path rootDirectory, ListIterator<Path> files, BackupConfig backupConfig)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
a89f7014aeb71dba5c94404dfea7eb89e7eeee74Jean-Noel Rouvignac List<String> unchangedFilenames = new ArrayList<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (files.hasNext() && !backupConfig.isCancelled())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path file = files.next();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String relativePath = rootDirectory.relativize(file).toString();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int cmp = relativePath.compareTo(archive.latestFileName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (cmp > 0 || (cmp == 0 && file.toFile().length() != archive.latestFileSize))
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi files.previous();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi break;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.info(NOTE_BACKUP_FILE_UNCHANGED, relativePath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi unchangedFilenames.add(relativePath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!unchangedFilenames.isEmpty())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi writeUnchangedFilenames(unchangedFilenames);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Writes the list of unchanged files names in a file as new entry in the archive. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void writeUnchangedFilenames(List<String> unchangedList) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String zipEntryName = ZIPENTRY_UNCHANGED_LOGFILES;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi writeStrings(unchangedList, zipEntryName, archive.cryptoEngine);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_WRITE_ARCHIVE_FILE.get(zipEntryName, archive.getBackupID(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archive.addBaseBackupAsDependency();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Writes the new files in the archive.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void writeChangedFiles(Path rootDirectory, ListIterator<Path> files, BackupConfig backupConfig)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (files.hasNext() && !backupConfig.isCancelled())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path file = files.next();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String relativePath = rootDirectory.relativize(file).toString();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archive.latestFileSize = writeFile(file, relativePath, archive.cryptoEngine, backupConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archive.latestFileName = relativePath;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (FileNotFoundException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // The file may have been deleted by a cleaner (i.e. for JE storage) since we started.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // The backupable entity is responsible for handling the changes through the files list iterator
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_WRITE_ARCHIVE_FILE.get(relativePath, archive.getBackupID(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private ZipOutputStream open(String backupPath, String archiveFilename) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi OutputStream output = openStream(backupPath, archiveFilename);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi output = cryptoEngine.encryptOutput(output);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return openZipStream(output);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private OutputStream openStream(String backupPath, String archiveFilename) throws DirectoryException {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi OutputStream output = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi File archiveFile = new File(backupPath, archiveFilename);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int i = 1;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (archiveFile.exists())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveFile = new File(backupPath, archiveFilename + "." + i);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi i++;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi output = new FileOutputStream(archiveFile, false);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archive.newBackupParams.putProperty(BACKUP_PROPERTY_ARCHIVE_FILENAME, archiveFilename);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return output;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(output);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_CANNOT_CREATE_ARCHIVE_FILE.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi get(archiveFilename, backupPath, archive.getBackupID(), stackTraceToSingleLineString(e));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Wraps the file output stream in a zip output stream. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private ZipOutputStream openZipStream(OutputStream outputStream)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipOutputStream zipStream = new ZipOutputStream(outputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipStream.setComment(ERR_BACKUP_ZIP_COMMENT.get(DynamicConstants.PRODUCT_NAME, archive.getBackupID())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi .toString());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (archive.newBackupParams.shouldCompress)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipStream.setLevel(Deflater.DEFAULT_COMPRESSION);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipStream.setLevel(Deflater.NO_COMPRESSION);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return zipStream;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi @Override
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public String toString()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return "BackupArchiveWriter [archive file=" + archive.getArchiveFilename() + ", backendId="
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi + archive.getBackendID() + "]";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Represents a reader of a backup archive. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static final class BackupArchiveReader {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final CryptoEngine cryptoEngine;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final File archiveFile;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final String identifier;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private final BackupInfo backupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupArchiveReader(String identifier, ExistingBackupArchive archive)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.identifier = identifier;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backupInfo = archive.getBackupInfo();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.archiveFile = archive.getArchiveFile();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.cryptoEngine = archive.getCryptoEngine();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupArchiveReader(String identifier, BackupInfo backupInfo, String backupDirectoryPath) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.identifier = identifier;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.backupInfo = backupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.archiveFile = BackupManager.retrieveArchiveFile(backupInfo, backupDirectoryPath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi this.cryptoEngine = CryptoEngine.forRestore(backupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Obtains the set of files in a backup that are unchanged from its
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * dependent backup or backups.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The file set is stored as as the first entry in the archive file.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return The set of files that are listed in "unchanged.txt" file
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * of the archive.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If an error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Set<String> readUnchangedDependentFiles() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
a89f7014aeb71dba5c94404dfea7eb89e7eeee74Jean-Noel Rouvignac Set<String> hashSet = new HashSet<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipInputStream zipStream = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipStream = openZipStream();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // Iterate through the entries in the zip file.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipEntry zipEntry = zipStream.getNextEntry();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (zipEntry != null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // We are looking for the entry containing the list of unchanged files.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (ZIPENTRY_UNCHANGED_LOGFILES.equals(zipEntry.getName()))
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi hashSet.addAll(readAllLines(zipStream));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi break;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipEntry = zipStream.getNextEntry();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return hashSet;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), ERR_BACKUP_CANNOT_RESTORE.get(
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi identifier, stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi finally {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(zipStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Restore the provided list of files from the provided restore directory.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param restoreDir
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The target directory for restored files.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param filesToRestore
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The set of files to restore. If empty, all files in the archive
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * are restored.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param restoreConfig
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The restore configuration, used to check for cancellation of
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * this restore operation.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If an error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi void restoreArchive(Path restoreDir, Set<String> filesToRestore, RestoreConfig restoreConfig, Backupable backupable)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreArchive0(restoreDir, filesToRestore, restoreConfig, backupable);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_RESTORE.get(identifier, stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // check the hash
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] hash = backupInfo.getUnsignedHash() != null ? backupInfo.getUnsignedHash() : backupInfo.getSignedHash();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoEngine.check(hash, backupInfo.getBackupID());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void restoreArchive0(Path restoreDir, Set<String> filesToRestore, RestoreConfig restoreConfig,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Backupable backupable) throws DirectoryException, IOException {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipInputStream zipStream = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipStream = openZipStream();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ZipEntry zipEntry = zipStream.getNextEntry();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (zipEntry != null && !restoreConfig.isCancelled())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String zipEntryName = zipEntry.getName();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Pair<Boolean, ZipEntry> result = handleSpecialEntries(zipStream, zipEntryName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (result.getFirst()) {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipEntry = result.getSecond();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi continue;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi boolean mustRestoreOnDisk = !restoreConfig.verifyOnly()
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi && (filesToRestore.isEmpty() || filesToRestore.contains(zipEntryName));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (mustRestoreOnDisk)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreZipEntry(zipEntryName, zipStream, restoreDir, restoreConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreZipEntryVirtual(zipEntryName, zipStream, restoreConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipEntry = zipStream.getNextEntry();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi finally {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(zipStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Handle any special entry in the archive.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return the pair (true, zipEntry) if next entry was read, (false, null) otherwise
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private Pair<Boolean, ZipEntry> handleSpecialEntries(ZipInputStream zipStream, String zipEntryName)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (ZIPENTRY_EMPTY_PLACEHOLDER.equals(zipEntryName))
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // the backup contains no files
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return Pair.of(true, zipStream.getNextEntry());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (ZIPENTRY_UNCHANGED_LOGFILES.equals(zipEntryName))
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // This entry is treated specially. It is never restored,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // and its hash is computed on the strings, not the bytes.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoEngine.updateHashWith(zipEntryName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi List<String> lines = readAllLines(zipStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi for (String line : lines)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoEngine.updateHashWith(line);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return Pair.of(true, zipStream.getNextEntry());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return Pair.of(false, null);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Restores a zip entry virtually (no actual write on disk).
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void restoreZipEntryVirtual(String zipEntryName, ZipInputStream zipStream, RestoreConfig restoreConfig)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws FileNotFoundException, IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (restoreConfig.verifyOnly())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.info(NOTE_BACKUP_VERIFY_FILE, zipEntryName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoEngine.updateHashWith(zipEntryName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreFile(zipStream, null, restoreConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Restores a zip entry with actual write on disk.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void restoreZipEntry(String zipEntryName, ZipInputStream zipStream, Path restoreDir,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi RestoreConfig restoreConfig) throws IOException, DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi OutputStream outputStream = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi long totalBytesRead = 0;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path fileToRestore = restoreDir.resolve(zipEntryName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ensureFileCanBeRestored(fileToRestore);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi outputStream = new FileOutputStream(fileToRestore.toFile());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoEngine.updateHashWith(zipEntryName);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi totalBytesRead = restoreFile(zipStream, outputStream, restoreConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.info(NOTE_BACKUP_RESTORED_FILE, zipEntryName, totalBytesRead);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi finally
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi StaticUtils.close(outputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void ensureFileCanBeRestored(Path fileToRestore) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path parent = fileToRestore.getParent();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!Files.exists(parent))
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Files.createDirectories(parent);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_CREATE_DIRECTORY_TO_RESTORE_FILE.get(fileToRestore, identifier));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Restores the file provided by the zip input stream.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The restore can be virtual: if the outputStream is {@code null}, the file
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * is not actually restored on disk.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private long restoreFile(ZipInputStream zipInputStream, OutputStream outputStream, RestoreConfig restoreConfig)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi long totalBytesRead = 0;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi byte[] buffer = new byte[8192];
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int bytesRead = zipInputStream.read(buffer);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (bytesRead > 0 && !restoreConfig.isCancelled())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi totalBytesRead += bytesRead;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi cryptoEngine.updateHashWith(buffer, 0, bytesRead);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (outputStream != null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi outputStream.write(buffer, 0, bytesRead);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi bytesRead = zipInputStream.read(buffer);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return totalBytesRead;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private InputStream openStream() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new FileInputStream(archiveFile);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (FileNotFoundException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_RESTORE.get(identifier, stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private ZipInputStream openZipStream() throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi InputStream inputStream = openStream();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi inputStream = cryptoEngine.encryptInput(inputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new ZipInputStream(inputStream);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private List<String> readAllLines(ZipInputStream zipStream) throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
a89f7014aeb71dba5c94404dfea7eb89e7eeee74Jean-Noel Rouvignac final ArrayList<String> results = new ArrayList<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String line;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BufferedReader reader = new BufferedReader(new InputStreamReader(zipStream));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while ((line = reader.readLine()) != null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi results.add(line);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return results;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Creates a backup of the provided backupable entity.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The backup is stored in a single zip file in the backup directory.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If the backup is incremental, then the first entry in the zip is a text
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * file containing a list of all the log files that are unchanged since the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * previous backup. The remaining zip entries are the log files themselves,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * which, for an incremental, only include those files that have changed.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupable
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The underlying entity (storage, backend) to be backed up.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupConfig
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The configuration to use when performing the backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If a Directory Server error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public void createBackup(final Backupable backupable, final BackupConfig backupConfig) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final NewBackupParams backupParams = new NewBackupParams(backupConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final CryptoEngine cryptoEngine = CryptoEngine.forCreation(backupConfig, backupParams);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final NewBackupArchive newArchive = new NewBackupArchive(backendID, backupParams, cryptoEngine);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupArchiveWriter archiveWriter = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final ListIterator<Path> files = backupable.getFilesToBackup();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final Path rootDirectory = backupable.getDirectory().toPath();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveWriter = new BackupArchiveWriter(newArchive);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (files.hasNext())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupParams.isIncremental) {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveWriter.writeUnchangedFiles(rootDirectory, files, backupConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveWriter.writeChangedFiles(rootDirectory, files, backupConfig);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi else {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveWriter.writeEmptyPlaceHolder();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi finally
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi closeArchiveWriter(archiveWriter, newArchive.getArchiveFilename(), backupParams.backupDir.getPath());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi newArchive.updateBackupDirectory();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupConfig.isCancelled())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // Remove the backup since it may be incomplete
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi removeBackup(backupParams.backupDir, backupParams.backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Restores a backupable entity from its backup, or verify the backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupable
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The underlying entity (storage, backend) to be backed up.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param restoreConfig
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The configuration to use when performing the restore.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If a Directory Server error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public void restoreBackup(Backupable backupable, RestoreConfig restoreConfig) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path saveDirectory = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!restoreConfig.verifyOnly())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi saveDirectory = backupable.beforeRestore();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final String backupID = restoreConfig.getBackupID();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final ExistingBackupArchive existingArchive =
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi new ExistingBackupArchive(backupID, restoreConfig.getBackupDirectory());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final Path restoreDirectory = getRestoreDirectory(backupable, backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (existingArchive.hasDependencies())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final BackupArchiveReader zipArchiveReader = new BackupArchiveReader(backupID, existingArchive);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final Set<String> unchangedFilesToRestore = zipArchiveReader.readUnchangedDependentFiles();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final List<BackupInfo> dependencies = existingArchive.getBackupDependencies();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi for (BackupInfo dependencyBackupInfo : dependencies)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreArchive(restoreDirectory, unchangedFilesToRestore, restoreConfig, backupable, dependencyBackupInfo);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi // Restore the final archive file.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Set<String> filesToRestore = emptySet();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreArchive(restoreDirectory, filesToRestore, restoreConfig, backupable, existingArchive.getBackupInfo());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!restoreConfig.verifyOnly())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi backupable.afterRestore(restoreDirectory, saveDirectory);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Removes the specified backup if it is possible to do so.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupDir The backup directory structure with which the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * specified backup is associated.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupID The backup ID for the backup to be removed.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException If it is not possible to remove the specified
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * backup for some reason (e.g., no such backup
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * exists or there are other backups that are
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * dependent upon it).
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public void removeBackup(BackupDirectory backupDir, String backupID) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ExistingBackupArchive archive = new ExistingBackupArchive(backupID, backupDir);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archive.removeArchive();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private Path getRestoreDirectory(Backupable backupable, String backupID)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi File restoreDirectory = backupable.getDirectory();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (!backupable.isDirectRestore())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi restoreDirectory = new File(restoreDirectory.getAbsoluteFile() + "-restore-" + backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return restoreDirectory.toPath();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void closeArchiveWriter(BackupArchiveWriter archiveWriter, String backupFile, String backupPath)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (archiveWriter != null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi archiveWriter.close();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi logger.traceException(e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_CLOSE_ZIP_STREAM.get(backupFile, backupPath, stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Restores the content of an archive file.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If set of files is not empty, only the specified files are restored.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If set of files is empty, all files are restored.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If the archive is being restored as a dependency, then only files in the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * specified set are restored, and the restored files are removed from the
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * set. Otherwise all files from the archive are restored, and files that are
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * to be found in dependencies are added to the set.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param restoreDir
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The directory in which files are to be restored.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param filesToRestore
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The set of files to restore. If empty, then all files are
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * restored.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param restoreConfig
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The restore configuration.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupInfo
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The backup containing the files to be restored.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If a Directory Server error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If an I/O exception occurs during the restore.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private void restoreArchive(Path restoreDir,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Set<String> filesToRestore,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi RestoreConfig restoreConfig,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Backupable backupable,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupInfo backupInfo) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String backupID = backupInfo.getBackupID();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String backupDirectoryPath = restoreConfig.getBackupDirectory().getPath();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupArchiveReader zipArchiveReader = new BackupArchiveReader(backupID, backupInfo, backupDirectoryPath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi zipArchiveReader.restoreArchive(restoreDir, filesToRestore, restoreConfig, backupable);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /** Retrieves the full path of the archive file. */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static File retrieveArchiveFile(BackupInfo backupInfo, String backupDirectoryPath)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Map<String,String> backupProperties = backupInfo.getBackupProperties();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String archiveFilename = backupProperties.get(BACKUP_PROPERTY_ARCHIVE_FILENAME);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return new File(backupDirectoryPath, archiveFilename);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Get the information for a given backup ID from the backup directory.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupDir The backup directory.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupID The backup ID.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return The backup information, never null.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException If the backup information cannot be found.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static BackupInfo getBackupInfo(BackupDirectory backupDir, String backupID) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupInfo backupInfo = backupDir.getBackupInfo(backupID);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (backupInfo == null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi LocalizableMessage message = ERR_BACKUP_MISSING_BACKUPID.get(backupID, backupDir.getPath());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return backupInfo;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Helper method to build a list of files to backup, in the simple case where all files are located
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * under the provided directory.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param directory
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The directory containing files to backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param filter
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The filter to select files to backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param identifier
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Identifier of the backed-up entity
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return the files to backup, which may be empty but never {@code null}
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * if an error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public static List<Path> getFiles(File directory, FileFilter filter, String identifier)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi File[] files = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi files = directory.listFiles(filter);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (Exception e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_LIST_LOG_FILES.get(directory.getAbsolutePath(), identifier), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (files == null)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(ResultCode.NO_SUCH_OBJECT,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_LIST_LOG_FILES.get(directory.getAbsolutePath(), identifier));
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi List<Path> paths = new ArrayList<>();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi for (File file : files)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi paths.add(file.toPath());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return paths;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Helper method to save all current files of the provided backupable entity, using
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * default behavior.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param backupable
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The entity to backup.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param identifier
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Identifier of the backup
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return the directory where all files are saved.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If a problem occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public static Path saveCurrentFilesToDirectory(Backupable backupable, String identifier) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ListIterator<Path> filesToBackup = backupable.getFilesToBackup();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi File rootDirectory = backupable.getDirectory();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String saveDirectory = rootDirectory.getAbsolutePath() + ".save";
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi BackupManager.saveFilesToDirectory(rootDirectory.toPath(), filesToBackup, saveDirectory, identifier);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return Paths.get(saveDirectory);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Helper method to move all provided files in a target directory created from
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * provided target base path, keeping relative path information relative to
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * root directory.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param rootDirectory
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * A directory which is an ancestor of all provided files.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param files
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * The files to move.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param targetBasePath
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Base path of the target directory. Actual directory is built by
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * adding ".save" and a number, always ensuring that the directory is new.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param identifier
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Identifier of the backup
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return the actual directory where all files are saved.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * If a problem occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi public static Path saveFilesToDirectory(Path rootDirectory, ListIterator<Path> files, String targetBasePath,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String identifier) throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path targetDirectory = null;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi targetDirectory = createDirectoryWithNumericSuffix(targetBasePath, identifier);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi while (files.hasNext())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path file = files.next();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path relativeFilePath = rootDirectory.relativize(file);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path targetFile = targetDirectory.resolve(relativeFilePath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Files.createDirectories(targetFile.getParent());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Files.move(file, targetFile);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return targetDirectory;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_SAVE_FILES_BEFORE_RESTORE.get(rootDirectory, targetDirectory, identifier,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Creates a new directory based on the provided directory path, by adding a
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * suffix number that is guaranteed to be the highest.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi static Path createDirectoryWithNumericSuffix(final String baseDirectoryPath, String identifier)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throws DirectoryException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi try
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int number = getHighestSuffixNumberForPath(baseDirectoryPath);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi String path = baseDirectoryPath + (number + 1);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Path directory = Paths.get(path);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi Files.createDirectories(directory);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return directory;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi catch (IOException e)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi ERR_BACKUP_CANNOT_CREATE_SAVE_DIRECTORY.get(baseDirectoryPath, identifier,
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi stackTraceToSingleLineString(e)), e);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi /**
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Returns a number that correspond to the highest suffix number existing for the provided base path.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <p>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * Example: given the following directory structure
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * <pre>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * +--- someDir
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * | \--- directory
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * | \--- directory1
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * | \--- directory2
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * | \--- directory10
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * </pre>
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * getHighestSuffixNumberForPath("directory") returns 10.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi *
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @param basePath
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * A base path to a file or directory, without any suffix number.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @return the highest suffix number, or 0 if no suffix number exists
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * @throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi * if an error occurs.
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi */
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi private static int getHighestSuffixNumberForPath(final String basePath) throws IOException
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
7b234a46f87e6a4cf1fbd196d624c036e5870c08Fabio Pistolesi final File baseFile = new File(basePath).getCanonicalFile();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final File[] existingFiles = baseFile.getParentFile().listFiles();
7b234a46f87e6a4cf1fbd196d624c036e5870c08Fabio Pistolesi final Pattern pattern = Pattern.compile(baseFile + "\\d*");
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int highestNumber = 0;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi for (File file : existingFiles)
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi final String name = file.getCanonicalPath();
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi if (pattern.matcher(name).matches())
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi {
7b234a46f87e6a4cf1fbd196d624c036e5870c08Fabio Pistolesi String numberAsString = name.substring(baseFile.getPath().length());
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi int number = numberAsString.isEmpty() ? 0 : Integer.valueOf(numberAsString);
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi highestNumber = number > highestNumber ? number : highestNumber;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi return highestNumber;
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi }
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi
75fceea66e311b3de58d76a4c993af0fec13dd3dNicolas Capponi}