blob: 2cc51d15d1586c7783918864561f01483481bc1a [file] [log] [blame]
/*
* 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 org.apache.iotdb.commons.utils;
import org.apache.iotdb.commons.auth.entity.PathPrivilege;
import org.apache.iotdb.commons.auth.entity.PriPrivilegeType;
import org.apache.iotdb.commons.auth.entity.Role;
import org.apache.iotdb.commons.exception.IllegalPathException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.tsfile.utils.Pair;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class IOUtils {
private IOUtils() {}
/**
* Write a string into the given stream.
*
* @param outputStream the destination to insert.
* @param str the string to be written.
* @param encoding string encoding like 'utf-8'.
* @param encodingBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory
* allocations. A null may also be passed to use a local buffer.
* @throws IOException when an exception raised during operating the stream.
*/
public static void writeString(
OutputStream outputStream,
String str,
String encoding,
ThreadLocal<ByteBuffer> encodingBufferLocal)
throws IOException {
if (str != null) {
byte[] strBuffer = str.getBytes(encoding);
writeInt(outputStream, strBuffer.length, encodingBufferLocal);
outputStream.write(strBuffer);
} else {
writeInt(outputStream, 0, encodingBufferLocal);
}
}
/**
* Write an integer into the given stream.
*
* @param outputStream the destination to insert.
* @param i the integer to be written.
* @param encodingBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory
* allocations. A null may also be passed to use a local buffer.
* @throws IOException when an exception raised during operating the stream.
*/
public static void writeInt(
OutputStream outputStream, int i, ThreadLocal<ByteBuffer> encodingBufferLocal)
throws IOException {
ByteBuffer encodingBuffer;
if (encodingBufferLocal != null) {
encodingBuffer = encodingBufferLocal.get();
if (encodingBuffer == null) {
// set to 8 because this buffer may be applied to other types
encodingBuffer = ByteBuffer.allocate(8);
encodingBufferLocal.set(encodingBuffer);
}
} else {
encodingBuffer = ByteBuffer.allocate(4);
}
encodingBuffer.clear();
encodingBuffer.putInt(i);
outputStream.write(encodingBuffer.array(), 0, Integer.BYTES);
}
/**
* Read a string from the given stream.
*
* @param inputStream the source to read.
* @param encoding string encoding like 'utf-8'.
* @param strBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory
* allocations. A null may also be passed to use a local buffer.
* @return a string read from the stream.
* @throws IOException when an exception raised during operating the stream.
*/
public static String readString(
DataInputStream inputStream, String encoding, ThreadLocal<byte[]> strBufferLocal)
throws IOException {
byte[] strBuffer;
int length = inputStream.readInt();
if (length > 0) {
if (strBufferLocal != null) {
strBuffer = strBufferLocal.get();
if (strBuffer == null || length > strBuffer.length) {
strBuffer = new byte[length];
strBufferLocal.set(strBuffer);
}
} else {
strBuffer = new byte[length];
}
inputStream.read(strBuffer, 0, length);
return new String(strBuffer, 0, length, encoding);
}
return null;
}
/**
* To distinguish between permissions files of the new and old versions, in files generated by the
* new version, the length of the first string will be stored as a negative number.
*
* <p>Only for LocalFileUserAccessor/LocalFileRoleAccessor. We save our roles/users' name string
* in a different format.
*
* @param inputStream the source to read.
* @param encoding string encoding like "utf-8"
* @param strBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory.
* allocations. A null may also be passed to use a local buffer.
* @return a pair contains one string and boolean. The string is the auth string we get from input
* stream. The boolean is true means length of the auth string is greater than 0.
* @throws IOException when an exception raised during operating the stream.
*/
public static Pair<String, Boolean> readAuthString(
DataInputStream inputStream, String encoding, ThreadLocal<byte[]> strBufferLocal)
throws IOException {
byte[] strBuffer;
int length = inputStream.readInt();
int absLength = Math.abs(length);
if (absLength != 0) {
if (strBufferLocal != null) {
strBuffer = strBufferLocal.get();
if (strBuffer == null || absLength > strBuffer.length) {
strBuffer = new byte[absLength];
strBufferLocal.set(strBuffer);
}
} else {
strBuffer = new byte[absLength];
}
inputStream.read(strBuffer, 0, absLength);
Pair<String, Boolean> result =
new Pair<>(new String(strBuffer, 0, absLength, encoding), length > 0);
return result;
}
return null;
}
/**
* Read a PathPrivilege from the given stream.
*
* @param inputStream the source to read.
* @param encoding string encoding like 'utf-8'.
* @param strBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory
* allocations. A null may also be passed to use a local buffer.
* @param compatible A boolean value used to indicate whether the old version of permission
* deserialization method should be employed.
* @return a PathPrivilege read from the stream.
* @throws IOException when an exception raised during operating the stream.
*/
public static PathPrivilege readPathPrivilege(
DataInputStream inputStream,
String encoding,
ThreadLocal<byte[]> strBufferLocal,
boolean compatible)
throws IOException, IllegalPathException {
String path = IOUtils.readString(inputStream, encoding, strBufferLocal);
if (compatible) {
int privilegeNum = inputStream.readInt();
PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath(path));
for (int i = 0; i < privilegeNum; i++) {
// Need to check the corresponding relationship of permissions
pathPrivilege.getPrivileges().add(inputStream.readInt());
}
return pathPrivilege;
} else {
PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath(path));
int privileges = inputStream.readInt();
pathPrivilege.setAllPrivileges(privileges);
return pathPrivilege;
}
}
// Because pre version's privilege will stored by path + privilege
// This func will turn the privilege info into role's path privileges.
// for the global privilege, they were stored by root + privilege.
public static void loadRolePrivilege(
Role role, DataInputStream inputStream, String encoding, ThreadLocal<byte[]> strBufferLocal)
throws IOException, IllegalPathException {
role.setSysPrivilegeSet(0);
int pathPriNum = inputStream.readInt();
List<PathPrivilege> pathPrivilegeList = new ArrayList<>();
for (int i = 0; i < pathPriNum; i++) {
String path = IOUtils.readString(inputStream, encoding, strBufferLocal);
PartialPath ppath = new PartialPath(path);
PathPrivilege pathPriv = new PathPrivilege(ppath);
int priNum = inputStream.readInt();
for (int j = 0; j < priNum; j++) {
PriPrivilegeType priType = PriPrivilegeType.values()[inputStream.readInt()];
if (priType.isAccept()) {
pathPriv.grantPrivilege(priType.ordinal(), false);
}
}
pathPrivilegeList.add(pathPriv);
}
role.setPrivilegeList(pathPrivilegeList);
role.setServiceReady(false);
}
/**
* Write a PathPrivilege to the given stream.
*
* @param outputStream the destination to insert.
* @param pathPrivilege the PathPrivilege to be written.
* @param encoding string encoding like 'utf-8'.
* @param encodingBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory
* allocations. A null may also be passed to use a local buffer.
* @throws IOException when an exception raised during operating the stream.
*/
public static void writePathPrivilege(
OutputStream outputStream,
PathPrivilege pathPrivilege,
String encoding,
ThreadLocal<ByteBuffer> encodingBufferLocal)
throws IOException {
writeString(outputStream, pathPrivilege.getPath().getFullPath(), encoding, encodingBufferLocal);
writeInt(outputStream, pathPrivilege.getAllPrivileges(), encodingBufferLocal);
}
/**
* Replace newFile with oldFile. If the file system does not support atomic file replacement then
* delete the old file first.
*
* @param newFile the new file.
* @param oldFile the file to be replaced.
*/
public static void replaceFile(File newFile, File oldFile) throws IOException {
if (!newFile.renameTo(oldFile)) {
// some OSs need to delete the old file before renaming to it
if (!oldFile.delete()) {
throw new IOException(String.format("Cannot delete old user file : %s", oldFile.getPath()));
}
if (!newFile.renameTo(oldFile)) {
throw new IOException(
String.format("Cannot replace old user file with new one : %s", newFile.getPath()));
}
}
}
public static ByteBuffer clone(ByteBuffer original) {
ByteBuffer clone = ByteBuffer.allocate(original.capacity());
original.rewind();
clone.put(original);
original.rewind();
clone.flip();
return clone;
}
}