| /* |
| * 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; |
| } |
| } |