/**
* 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.aliyun.odps.volume;
import java.net.URI;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.util.StringUtils;
import com.aliyun.odps.VolumeException;
import com.aliyun.odps.VolumeFSFile;
import com.aliyun.odps.fs.VolumeFileSystemConfigKeys;
import com.aliyun.odps.tunnel.VolumeFSErrorCode;
import com.aliyun.odps.volume.protocol.VolumeFSConstants;
/**
* Utility that wraps some Volume related utility methods
*
* @author Emerson Zhao [mailto:zhenyi.zzy@alibaba-inc.com]
*
*/
public class VolumeFSUtil {
/**
* Whether the pathname is valid. Currently prohibits relative paths, names which contain a ":" or
* "//", or other non-canonical paths.
*/
public static boolean isValidName(String src) {
if (src == null)
return false;
// Path must be absolute.
if (!src.startsWith(Path.SEPARATOR)) {
return false;
}
// Check for "~" ".." "." ":" "/"
String[] components = StringUtils.split(src, '/');
for (int i = 0; i < components.length; i++) {
String element = components[i];
if (element.equals("~") || element.equals(".") || element.equals("..")
|| (element.indexOf(":") >= 0) || (element.indexOf("/") >= 0)
|| (element.indexOf("\\") >= 0) || (element.indexOf("\0") >= 0)) {
return false;
}
// The string may start or end with a /, but not have
// "//" in the middle.
if (element.isEmpty() && i != components.length - 1 && i != 0) {
return false;
}
}
return true;
}
/**
* Get volume name from a specific {@link Path}
*
* @param path
* @throws VolumeException
*/
public static String getVolumeFromPath(Path path) throws VolumeException {
path = Path.getPathWithoutSchemeAndAuthority(path);
if (path.depth() == 0) {
throw new VolumeException(VolumeFSErrorCode.VolumeMissing, "No volume found!");
} else {
String p = path.toUri().getPath();
String volume = p.split(VolumeFSConstants.SEPARATOR)[1];
return volume;
}
}
/**
* Get volume name from a specific path str
*
* @param pathStr
* @throws VolumeException
*/
public static String getVolumeFromPath(String pathStr) throws VolumeException {
Path path = new Path(pathStr);
return getVolumeFromPath(path);
}
/**
* Transfer {@link VolumeFSFile} to {@link FileStatus}
*
* @param file
*/
public static FileStatus transferFile(VolumeFSFile file) {
if (file == null) {
return null;
}
Path symlink =
org.apache.commons.lang.StringUtils.isBlank(file.getSymlink()) ? null : new Path(
file.getSymlink());
FileStatus fileStatus =
new FileStatus(file.getLength(), file.getIsdir(), file.getBlockReplications(),
file.getBlockSize(), file.getModificationTime().getTime(), file.getAccessTime()
.getTime(), new FsPermission(Short.valueOf(file.getPermission(), 8)),
file.getOwner(), file.getGroup(), symlink, new Path(
VolumeFileSystemConfigKeys.VOLUME_URI_SCHEME + "://" + file.getProject(),
file.getPath()));
return fileStatus;
}
/**
* Check if a path is just a volume
*
* @param path
*/
public static boolean checkPathIsJustVolume(String path) {
if (org.apache.commons.lang.StringUtils.isBlank(path)) {
return false;
}
if (path.startsWith("/")) {
path = path.substring(1);
}
if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1);
}
if (path.indexOf("/") == -1) {
return true;
} else {
return false;
}
}
/**
* Check if a path is root
*
* @param path
*/
public static boolean checkPathIsRoot(String path) {
if (org.apache.commons.lang.StringUtils.isBlank(path)) {
return false;
}
path = path.replaceAll("//", "/");
return path.trim().equals(VolumeFSConstants.ROOT_PATH);
}
/**
* Batch transfer {@link VolumeFSFile} to {@link FileStatus}
*
* @param files s
*/
public static FileStatus[] transferFiles(VolumeFSFile[] files) {
if (files == null)
return null;
FileStatus[] fileStatusArray = new FileStatus[files.length];
for (int i = 0; i < files.length; i++) {
fileStatusArray[i] = transferFile(files[i]);
}
return fileStatusArray;
}
/**
* Probe for a path being a parent of another
*
* @param parent parent path
* @param child possible child path
* @return true if the parent's path matches the start of the child's
*/
public static boolean isParentOf(Path parent, Path child) {
URI parentURI = parent.toUri();
String parentPath = parentURI.getPath();
if (!parentPath.endsWith("/")) {
parentPath += "/";
}
URI childURI = child.toUri();
String childPath = childURI.getPath();
return childPath.startsWith(parentPath);
}
public static void checkPath(String path) throws VolumeException {
if (checkPathIsRoot(path)) {
throw new VolumeException(VolumeFSErrorCode.VolumeMissing,
"Root path is not supported this operation!");
}
if (checkPathIsJustVolume(path)) {
throw new VolumeException(VolumeFSErrorCode.InvalidPath, "The path is just a volume!");
}
if (!isValidName(path)) {
throw new VolumeException(VolumeFSErrorCode.InvalidPath,
"The path contains illegal characters!");
}
}
}