content: //
  // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  //
  
  package com.cloud.storage;
  
  import java.io.File;
  import java.io.IOException;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.nio.file.attribute.FileAttribute;
  import java.nio.file.attribute.PosixFilePermission;
  import java.nio.file.attribute.PosixFilePermissions;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.UUID;
  
  import javax.naming.ConfigurationException;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  public class JavaStorageLayer implements StorageLayer {
      protected Logger logger = LogManager.getLogger(getClass());
      private static final String STD_TMP_DIR_PATH = "/tmp";
      String _name;
      boolean _makeWorldWriteable = true;
  
      public JavaStorageLayer() {
          super();
      }
  
      public JavaStorageLayer(boolean makeWorldWriteable) {
          this();
          _makeWorldWriteable = makeWorldWriteable;
      }
  
      @Override
      public boolean cleanup(String path, String rootPath) throws IOException {
          assert path.startsWith(rootPath) : path + " does not start with " + rootPath;
  
          synchronized (path) {
              File file = new File(path);
              if (!file.delete()) {
                  return false;
              }
              int index = -1;
              long rootLength = rootPath.length();
              while ((index = path.lastIndexOf(File.separator)) != -1 && path.length() > rootLength) {
                  file = new File(path.substring(0, index));
                  String[] children = file.list();
                  if (children != null && children.length > 0) {
                      break;
                  }
                  if (!file.delete()) {
                      throw new IOException("Unable to delete " + file.getAbsolutePath());
                  }
              }
              return true;
          }
      }
  
      @Override
      public boolean create(String path, String filename) throws IOException {
          synchronized (path.intern()) {
              String newFile = path + File.separator + filename;
              File file = new File(newFile);
              if (file.exists()) {
                  return true;
              }
  
              return file.createNewFile();
          }
      }
  
      @Override
      public boolean delete(String path) {
          synchronized (path.intern()) {
              File file = new File(path);
              return file.delete();
          }
      }
  
      @Override
      public boolean deleteDir(String dir) {
          File Dir = new File(dir);
          if (!Dir.isDirectory()) {
              return false;
          }
  
          synchronized (dir.intern()) {
              File[] files = Dir.listFiles();
              for (File file : files) {
                  if (!file.delete()) {
                      return false;
                  }
              }
          }
          return true;
      }
  
      @Override
      public boolean exists(String path) {
          synchronized (path.intern()) {
              File file = new File(path);
              return file.exists();
          }
      }
  
      @Override
      public long getTotalSpace(String path) {
          File file = new File(path);
          return file.getTotalSpace();
      }
  
      @Override
      public long getUsableSpace(String path) {
          File file = new File(path);
          return file.getUsableSpace();
      }
  
      @Override
      public String[] listFiles(String path) {
          File file = new File(path);
          File[] files = file.listFiles();
          if (files == null) {
              return new String[0];
          }
          String[] paths = new String[files.length];
          for (int i = 0; i < files.length; i++) {
              paths[i] = files[i].getAbsolutePath();
          }
          return paths;
      }
  
      @Override
      public List<String> listMountPointsByMsHost(String path, long msHostId) {
          List<String> mountPaths = new ArrayList<String>();
          File[] files = new File(path).listFiles();
          if (files == null) {
              return mountPaths;
          }
          for (File file : files) {
              if (file.getName().startsWith(String.valueOf(msHostId) + ".")) {
                  mountPaths.add(file.getAbsolutePath());
              }
          }
          return mountPaths;
      }
  
      @Override
      public boolean mkdir(String path) {
          synchronized (path.intern()) {
              File file = new File(path);
  
              if (file.exists()) {
                  return file.isDirectory();
              }
              if (_makeWorldWriteable) {
                  return (file.mkdirs() && setWorldReadableAndWriteable(file));
              } else {
                  return file.mkdirs();
              }
          }
      }
  
      @Override
      public long getSize(String path) {
          File file = new File(path);
          return file.length();
      }
  
      @Override
      public File createUniqDir() throws IOException {
          String dirName = System.getProperty("java.io.tmpdir");
          String subDirNamePrefix = "";
          FileAttribute<Set<PosixFilePermission>> perms = PosixFilePermissions
                  .asFileAttribute(PosixFilePermissions.fromString("rwxrwx---"));
          if (dirName != null) {
              Path p = Files.createTempDirectory(Path.of(dirName), subDirNamePrefix, perms);
              File dir = p.toFile();
              if (dir.exists()) {
                  if (isWorldReadable(dir)) {
                      if (STD_TMP_DIR_PATH.equals(dir.getAbsolutePath())) {
                          logger.warn(String.format("The temp dir is %s", STD_TMP_DIR_PATH));
                      } else {
                          logger.warn("The temp dir " + dir.getAbsolutePath() + " is World Readable");
                      }
                  }
                  String uniqDirName = dir.getAbsolutePath() + File.separator + UUID.randomUUID().toString();
                  if (mkdir(uniqDirName)) {
                      return new File(uniqDirName);
                  }
              }
          }
          throw new IOException("the tmp dir " + dirName + " does not exist");
      }
  
      @Override
      public boolean mkdirs(String path) {
          synchronized (path.intern()) {
              File dir = new File(path);
  
              if (dir.exists()) {
                  return dir.isDirectory();
              }
  
              boolean success = true;
              List<String> dirPaths = listDirPaths(path);
              for (String dirPath : dirPaths) {
                  dir = new File(dirPath);
                  if (!dir.exists()) {
                      success = dir.mkdir();
                      if (_makeWorldWriteable) {
                          success = success && setWorldReadableAndWriteable(dir);
                      }
                  }
              }
  
              return success;
          }
      }
  
      public boolean isWorldReadable(File file) throws IOException {
          Set<PosixFilePermission> permissions;
          permissions = Files.getPosixFilePermissions(
              Paths.get(file.getAbsolutePath()));
          return permissions.contains(PosixFilePermission.OTHERS_READ);
      }
  
      private List<String> listDirPaths(String path) {
          String[] dirNames = path.split("/");
          List<String> dirPaths = new ArrayList<String>();
  
          String currentPath = "";
          for (int i = 0; i < dirNames.length; i++) {
              String currentName = dirNames[i].trim();
              if (!currentName.isEmpty()) {
                  currentPath += "/" + currentName;
                  dirPaths.add(currentPath);
              }
          }
  
          return dirPaths;
      }
  
      @Override
      public boolean setWorldReadableAndWriteable(File file) {
          return (file.setReadable(true, false) && file.setWritable(true, false));
      }
  
      @Override
      public boolean isDirectory(String path) {
          File file = new File(path);
          return file.isDirectory();
      }
  
      @Override
      public boolean isFile(String path) {
          File file = new File(path);
          return file.isFile();
      }
  
      @Override
      public File getFile(String path) {
          return new File(path);
      }
  
      @Override
      public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
          _name = name;
          return true;
      }
  
      @Override
      public String getName() {
          return _name;
      }
  
      @Override
      public boolean start() {
          return true;
      }
  
      @Override
      public boolean stop() {
          return true;
      }
  
      @Override
      public long getUsedSpace(String path) {
          File file = new File(path);
          return file.getTotalSpace() - file.getFreeSpace();
      }
  
      @Override
      public void setName(String name) {
      }
  
      @Override
      public void setConfigParams(Map<String, Object> params) {
      }
  
      @Override
      public Map<String, Object> getConfigParams() {
          return null;
      }
  
      @Override
      public int getRunLevel() {
          return 0;
      }
  
      @Override
      public void setRunLevel(int level) {
      }
  
  }
  