001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015-2016 ForgeRock AS. 015 */ 016package org.opends.server.backends.pluggable.spi; 017 018import static org.opends.messages.BackendMessages.*; 019import static org.opends.messages.ConfigMessages.*; 020import static org.opends.server.util.StaticUtils.*; 021 022import java.io.File; 023 024import org.forgerock.i18n.LocalizableMessage; 025import org.forgerock.opendj.config.server.ConfigChangeResult; 026import org.forgerock.opendj.config.server.ConfigException; 027import org.opends.server.core.DirectoryServer; 028import org.forgerock.opendj.ldap.DN; 029import org.opends.server.types.FilePermission; 030 031/** Utility class for implementations of {@link Storage}. */ 032public final class StorageUtils 033{ 034 private StorageUtils() 035 { 036 // do not instantiate utility classes 037 } 038 039 /** 040 * Returns a database directory file from the provided parent database directory and backendId. 041 * 042 * @param parentDbDirectory the parent database directory 043 * @param backendId the backend id 044 * @return a database directory file where to store data for the provided backendId 045 */ 046 public static File getDBDirectory(String parentDbDirectory, String backendId) 047 { 048 return new File(getFileForPath(parentDbDirectory), backendId); 049 } 050 051 /** 052 * Ensure backendDir exists (creating it if not) and has the specified dbDirPermissions. 053 * 054 * @param backendDir the backend directory where to set the storage files 055 * @param dbDirPermissions the permissions to set for the database directory 056 * @param configDN the backend configuration DN 057 * @throws ConfigException if configuration fails 058 */ 059 public static void setupStorageFiles(File backendDir, String dbDirPermissions, DN configDN) throws ConfigException 060 { 061 ConfigChangeResult ccr = new ConfigChangeResult(); 062 063 checkDBDirExistsOrCanCreate(backendDir, ccr, false); 064 if (!ccr.getMessages().isEmpty()) 065 { 066 throw new ConfigException(ccr.getMessages().get(0)); 067 } 068 checkDBDirPermissions(dbDirPermissions, configDN, ccr); 069 if (!ccr.getMessages().isEmpty()) 070 { 071 throw new ConfigException(ccr.getMessages().get(0)); 072 } 073 setDBDirPermissions(backendDir, dbDirPermissions, configDN, ccr); 074 if (!ccr.getMessages().isEmpty()) 075 { 076 throw new ConfigException(ccr.getMessages().get(0)); 077 } 078 } 079 080 /** 081 * Checks a directory exists or can actually be created. 082 * 083 * @param backendDir the directory to check for 084 * @param ccr the list of reasons to return upstream or null if called from setupStorage() 085 * @param cleanup true if the directory should be deleted after creation 086 */ 087 public static void checkDBDirExistsOrCanCreate(File backendDir, ConfigChangeResult ccr, boolean cleanup) 088 { 089 if (!backendDir.exists()) 090 { 091 if (!backendDir.mkdirs()) 092 { 093 addErrorMessage(ccr, ERR_CREATE_FAIL.get(backendDir.getPath())); 094 } 095 if (cleanup) 096 { 097 backendDir.delete(); 098 } 099 } 100 else if (!backendDir.isDirectory()) 101 { 102 addErrorMessage(ccr, ERR_DIRECTORY_INVALID.get(backendDir.getPath())); 103 } 104 } 105 106 /** 107 * Returns false if directory permissions in the configuration are invalid. 108 * Otherwise returns the same value as it was passed in. 109 * 110 * @param dbDirPermissions the permissions to set for the database directory 111 * @param configDN the backend configuration DN 112 * @param ccr the current list of change results 113 */ 114 public static void checkDBDirPermissions(String dbDirPermissions, DN configDN, ConfigChangeResult ccr) 115 { 116 try 117 { 118 FilePermission backendPermission = decodeDBDirPermissions(dbDirPermissions, configDN); 119 // Make sure the mode will allow the server itself access to the database 120 if (!backendPermission.isOwnerWritable() 121 || !backendPermission.isOwnerReadable() 122 || !backendPermission.isOwnerExecutable()) 123 { 124 addErrorMessage(ccr, ERR_CONFIG_BACKEND_INSANE_MODE.get(dbDirPermissions)); 125 } 126 } 127 catch (ConfigException ce) 128 { 129 addErrorMessage(ccr, ce.getMessageObject()); 130 } 131 } 132 133 /** 134 * Sets files permissions on the backend directory. 135 * 136 * @param backendDir the directory to setup 137 * @param dbDirPermissions the permissions to set for the database directory 138 * @param configDN the backend configuration DN 139 * @param ccr the current list of change results 140 * @throws ConfigException if configuration fails 141 */ 142 public static void setDBDirPermissions(File backendDir, String dbDirPermissions, DN configDN, ConfigChangeResult ccr) 143 throws ConfigException 144 { 145 try 146 { 147 FilePermission backendPermission = decodeDBDirPermissions(dbDirPermissions, configDN); 148 if (!FilePermission.setPermissions(backendDir, backendPermission)) 149 { 150 addErrorMessage(ccr, WARN_UNABLE_SET_PERMISSIONS.get(backendPermission, backendDir)); 151 } 152 } 153 catch (Exception e) 154 { 155 addErrorMessage(ccr, WARN_SET_PERMISSIONS_FAILED.get(backendDir, stackTraceToSingleLineString(e))); 156 } 157 } 158 159 private static FilePermission decodeDBDirPermissions(String dbDirPermissions, DN configDN) throws ConfigException 160 { 161 try 162 { 163 return FilePermission.decodeUNIXMode(dbDirPermissions); 164 } 165 catch (Exception e) 166 { 167 throw new ConfigException(ERR_CONFIG_BACKEND_MODE_INVALID.get(configDN)); 168 } 169 } 170 171 /** 172 * Adds the provided message to the provided config change result. 173 * 174 * @param ccr the config change result 175 * @param message the message to add 176 */ 177 public static void addErrorMessage(ConfigChangeResult ccr, LocalizableMessage message) 178 { 179 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 180 ccr.addMessage(message); 181 } 182 183 /** 184 * Removes the storage files from the provided backend directory. 185 * 186 * @param backendDir 187 * the backend directory where to remove storage files 188 */ 189 public static void removeStorageFiles(File backendDir) 190 { 191 if (!backendDir.exists()) 192 { 193 return; 194 } 195 if (!backendDir.isDirectory()) 196 { 197 throw new StorageRuntimeException(ERR_DIRECTORY_INVALID.get(backendDir.getPath()).toString()); 198 } 199 200 try 201 { 202 File[] files = backendDir.listFiles(); 203 for (File f : files) 204 { 205 f.delete(); 206 } 207 } 208 catch (Exception e) 209 { 210 throw new StorageRuntimeException(ERR_REMOVE_FAIL.get(e.getMessage()).toString(), e); 211 } 212 } 213 214 /** 215 * Creates a new unusable {@link StorageStatus} for the disk full threshold. 216 * 217 * @param directory the directory which reached the disk full threshold 218 * @param thresholdInBytes the threshold in bytes 219 * @param backendId the backend id 220 * @return a new unusable {@link StorageStatus} 221 */ 222 public static StorageStatus statusWhenDiskSpaceFull(File directory, long thresholdInBytes, String backendId) 223 { 224 return StorageStatus.unusable(WARN_DISK_SPACE_FULL_THRESHOLD_CROSSED.get( 225 directory.getFreeSpace(), directory.getAbsolutePath(), thresholdInBytes, backendId)); 226 } 227 228 /** 229 * Creates a new locked down {@link StorageStatus} for the disk low threshold. 230 * 231 * @param directory the directory which reached the disk low threshold 232 * @param thresholdInBytes the threshold in bytes 233 * @param backendId the backend id 234 * @return a new locked down {@link StorageStatus} 235 */ 236 public static StorageStatus statusWhenDiskSpaceLow(File directory, long thresholdInBytes, String backendId) 237 { 238 return StorageStatus.lockedDown(WARN_DISK_SPACE_LOW_THRESHOLD_CROSSED.get( 239 directory.getFreeSpace(), directory.getAbsolutePath(), thresholdInBytes, backendId)); 240 } 241}