| /** |
| * 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.hadoop.fs; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.io.InvalidObjectException; |
| import java.io.ObjectInputValidation; |
| import java.io.Serializable; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.Set; |
| |
| import org.apache.hadoop.fs.FSProtos.FileStatusProto; |
| import org.apache.hadoop.classification.InterfaceAudience; |
| import org.apache.hadoop.classification.InterfaceStability; |
| import org.apache.hadoop.fs.permission.FsPermission; |
| import org.apache.hadoop.fs.protocolPB.PBHelper; |
| import org.apache.hadoop.io.Writable; |
| |
| /** Interface that represents the client side information for a file. |
| */ |
| @InterfaceAudience.Public |
| @InterfaceStability.Stable |
| public class FileStatus implements Writable, Comparable<Object>, |
| Serializable, ObjectInputValidation { |
| |
| private static final long serialVersionUID = 0x13caeae8; |
| |
| private Path path; |
| private long length; |
| private Boolean isdir; |
| private short block_replication; |
| private long blocksize; |
| private long modification_time; |
| private long access_time; |
| private FsPermission permission; |
| private String owner; |
| private String group; |
| private Path symlink; |
| private Set<AttrFlags> attr; |
| |
| /** |
| * Flags for entity attributes. |
| */ |
| public enum AttrFlags { |
| /** ACL information available for this entity. */ |
| HAS_ACL, |
| /** Entity is encrypted. */ |
| HAS_CRYPT, |
| /** Entity is stored erasure-coded. */ |
| HAS_EC, |
| /** Snapshot capability enabled. */ |
| SNAPSHOT_ENABLED, |
| } |
| |
| /** |
| * Shared, empty set of attributes (a common case for FileStatus). |
| */ |
| public static final Set<AttrFlags> NONE = Collections.<AttrFlags>emptySet(); |
| |
| /** |
| * Convert boolean attributes to a set of flags. |
| * @param acl See {@link AttrFlags#HAS_ACL}. |
| * @param crypt See {@link AttrFlags#HAS_CRYPT}. |
| * @param ec See {@link AttrFlags#HAS_EC}. |
| * @param sn See {@link AttrFlags#SNAPSHOT_ENABLED}. |
| * @return converted set of flags. |
| */ |
| public static Set<AttrFlags> attributes(boolean acl, boolean crypt, |
| boolean ec, boolean sn) { |
| if (!(acl || crypt || ec || sn)) { |
| return NONE; |
| } |
| EnumSet<AttrFlags> ret = EnumSet.noneOf(AttrFlags.class); |
| if (acl) { |
| ret.add(AttrFlags.HAS_ACL); |
| } |
| if (crypt) { |
| ret.add(AttrFlags.HAS_CRYPT); |
| } |
| if (ec) { |
| ret.add(AttrFlags.HAS_EC); |
| } |
| if (sn) { |
| ret.add(AttrFlags.SNAPSHOT_ENABLED); |
| } |
| return ret; |
| } |
| |
| public FileStatus() { this(0, false, 0, 0, 0, 0, null, null, null, null); } |
| |
| //We should deprecate this soon? |
| public FileStatus(long length, boolean isdir, int block_replication, |
| long blocksize, long modification_time, Path path) { |
| |
| this(length, isdir, block_replication, blocksize, modification_time, |
| 0, null, null, null, path); |
| } |
| |
| /** |
| * Constructor for file systems on which symbolic links are not supported |
| */ |
| public FileStatus(long length, boolean isdir, |
| int block_replication, |
| long blocksize, long modification_time, long access_time, |
| FsPermission permission, String owner, String group, |
| Path path) { |
| this(length, isdir, block_replication, blocksize, modification_time, |
| access_time, permission, owner, group, null, path); |
| } |
| |
| public FileStatus(long length, boolean isdir, |
| int block_replication, |
| long blocksize, long modification_time, long access_time, |
| FsPermission permission, String owner, String group, |
| Path symlink, |
| Path path) { |
| this(length, isdir, block_replication, blocksize, modification_time, |
| access_time, permission, owner, group, symlink, path, |
| false, false, false); |
| } |
| |
| public FileStatus(long length, boolean isdir, int block_replication, |
| long blocksize, long modification_time, long access_time, |
| FsPermission permission, String owner, String group, Path symlink, |
| Path path, boolean hasAcl, boolean isEncrypted, boolean isErasureCoded) { |
| this(length, isdir, block_replication, blocksize, modification_time, |
| access_time, permission, owner, group, symlink, path, |
| attributes(hasAcl, isEncrypted, isErasureCoded, false)); |
| } |
| |
| public FileStatus(long length, boolean isdir, int block_replication, |
| long blocksize, long modification_time, long access_time, |
| FsPermission permission, String owner, String group, Path symlink, |
| Path path, Set<AttrFlags> attr) { |
| this.length = length; |
| this.isdir = isdir; |
| this.block_replication = (short)block_replication; |
| this.blocksize = blocksize; |
| this.modification_time = modification_time; |
| this.access_time = access_time; |
| if (permission != null) { |
| this.permission = permission; |
| } else if (isdir) { |
| this.permission = FsPermission.getDirDefault(); |
| } else if (symlink != null) { |
| this.permission = FsPermission.getDefault(); |
| } else { |
| this.permission = FsPermission.getFileDefault(); |
| } |
| this.owner = (owner == null) ? "" : owner; |
| this.group = (group == null) ? "" : group; |
| this.symlink = symlink; |
| this.path = path; |
| this.attr = attr; |
| |
| // The variables isdir and symlink indicate the type: |
| // 1. isdir implies directory, in which case symlink must be null. |
| // 2. !isdir implies a file or symlink, symlink != null implies a |
| // symlink, otherwise it's a file. |
| assert (isdir && symlink == null) || !isdir; |
| } |
| |
| /** |
| * Copy constructor. |
| * |
| * @param other FileStatus to copy |
| */ |
| public FileStatus(FileStatus other) throws IOException { |
| // It's important to call the getters here instead of directly accessing the |
| // members. Subclasses like ViewFsFileStatus can override the getters. |
| this(other.getLen(), other.isDirectory(), other.getReplication(), |
| other.getBlockSize(), other.getModificationTime(), other.getAccessTime(), |
| other.getPermission(), other.getOwner(), other.getGroup(), |
| (other.isSymlink() ? other.getSymlink() : null), |
| other.getPath()); |
| } |
| |
| /** |
| * Get the length of this file, in bytes. |
| * @return the length of this file, in bytes. |
| */ |
| public long getLen() { |
| return length; |
| } |
| |
| /** |
| * Is this a file? |
| * @return true if this is a file |
| */ |
| public boolean isFile() { |
| return !isDirectory() && !isSymlink(); |
| } |
| |
| /** |
| * Is this a directory? |
| * @return true if this is a directory |
| */ |
| public boolean isDirectory() { |
| return isdir; |
| } |
| |
| /** |
| * Old interface, instead use the explicit {@link FileStatus#isFile()}, |
| * {@link FileStatus#isDirectory()}, and {@link FileStatus#isSymlink()} |
| * @return true if this is a directory. |
| * @deprecated Use {@link FileStatus#isFile()}, |
| * {@link FileStatus#isDirectory()}, and {@link FileStatus#isSymlink()} |
| * instead. |
| */ |
| @Deprecated |
| public final boolean isDir() { |
| return isDirectory(); |
| } |
| |
| /** |
| * Is this a symbolic link? |
| * @return true if this is a symbolic link |
| */ |
| public boolean isSymlink() { |
| return symlink != null; |
| } |
| |
| /** |
| * Get the block size of the file. |
| * @return the number of bytes |
| */ |
| public long getBlockSize() { |
| return blocksize; |
| } |
| |
| /** |
| * Get the replication factor of a file. |
| * @return the replication factor of a file. |
| */ |
| public short getReplication() { |
| return block_replication; |
| } |
| |
| /** |
| * Get the modification time of the file. |
| * @return the modification time of file in milliseconds since January 1, 1970 UTC. |
| */ |
| public long getModificationTime() { |
| return modification_time; |
| } |
| |
| /** |
| * Get the access time of the file. |
| * @return the access time of file in milliseconds since January 1, 1970 UTC. |
| */ |
| public long getAccessTime() { |
| return access_time; |
| } |
| |
| /** |
| * Get FsPermission associated with the file. |
| * @return permission. If a filesystem does not have a notion of permissions |
| * or if permissions could not be determined, then default |
| * permissions equivalent of "rwxrwxrwx" is returned. |
| */ |
| public FsPermission getPermission() { |
| return permission; |
| } |
| |
| /** |
| * Tell whether the underlying file or directory has ACLs set. |
| * |
| * @return true if the underlying file or directory has ACLs set. |
| */ |
| public boolean hasAcl() { |
| return attr.contains(AttrFlags.HAS_ACL); |
| } |
| |
| /** |
| * Tell whether the underlying file or directory is encrypted or not. |
| * |
| * @return true if the underlying file is encrypted. |
| */ |
| public boolean isEncrypted() { |
| return attr.contains(AttrFlags.HAS_CRYPT); |
| } |
| |
| /** |
| * Tell whether the underlying file or directory is erasure coded or not. |
| * |
| * @return true if the underlying file or directory is erasure coded. |
| */ |
| public boolean isErasureCoded() { |
| return attr.contains(AttrFlags.HAS_EC); |
| } |
| |
| /** |
| * Check if directory is Snapshot enabled or not. |
| * |
| * @return true if directory is snapshot enabled |
| */ |
| public boolean isSnapshotEnabled() { |
| return attr.contains(AttrFlags.SNAPSHOT_ENABLED); |
| } |
| |
| /** |
| * Get the owner of the file. |
| * @return owner of the file. The string could be empty if there is no |
| * notion of owner of a file in a filesystem or if it could not |
| * be determined (rare). |
| */ |
| public String getOwner() { |
| return owner; |
| } |
| |
| /** |
| * Get the group associated with the file. |
| * @return group for the file. The string could be empty if there is no |
| * notion of group of a file in a filesystem or if it could not |
| * be determined (rare). |
| */ |
| public String getGroup() { |
| return group; |
| } |
| |
| public Path getPath() { |
| return path; |
| } |
| |
| public void setPath(final Path p) { |
| path = p; |
| } |
| |
| /* These are provided so that these values could be loaded lazily |
| * by a filesystem (e.g. local file system). |
| */ |
| |
| /** |
| * Sets permission. |
| * @param permission if permission is null, default value is set |
| */ |
| protected void setPermission(FsPermission permission) { |
| this.permission = (permission == null) ? |
| FsPermission.getFileDefault() : permission; |
| } |
| |
| /** |
| * Sets owner. |
| * @param owner if it is null, default value is set |
| */ |
| protected void setOwner(String owner) { |
| this.owner = (owner == null) ? "" : owner; |
| } |
| |
| /** |
| * Sets group. |
| * @param group if it is null, default value is set |
| */ |
| protected void setGroup(String group) { |
| this.group = (group == null) ? "" : group; |
| } |
| |
| /** |
| * @return The contents of the symbolic link. |
| */ |
| public Path getSymlink() throws IOException { |
| if (!isSymlink()) { |
| throw new IOException("Path " + path + " is not a symbolic link"); |
| } |
| return symlink; |
| } |
| |
| public void setSymlink(final Path p) { |
| symlink = p; |
| } |
| |
| /** |
| * Compare this FileStatus to another FileStatus |
| * @param o the FileStatus to be compared. |
| * @return a negative integer, zero, or a positive integer as this object |
| * is less than, equal to, or greater than the specified object. |
| */ |
| public int compareTo(FileStatus o) { |
| return this.getPath().compareTo(o.getPath()); |
| } |
| |
| /** |
| * Compare this FileStatus to another FileStatus. |
| * This method was added back by HADOOP-14683 to keep binary compatibility. |
| * |
| * @param o the FileStatus to be compared. |
| * @return a negative integer, zero, or a positive integer as this object |
| * is less than, equal to, or greater than the specified object. |
| * @throws ClassCastException if the specified object is not FileStatus |
| */ |
| @Override |
| public int compareTo(Object o) { |
| FileStatus other = (FileStatus) o; |
| return compareTo(other); |
| } |
| |
| /** Compare if this object is equal to another object |
| * @param o the object to be compared. |
| * @return true if two file status has the same path name; false if not. |
| */ |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof FileStatus)) { |
| return false; |
| } |
| if (this == o) { |
| return true; |
| } |
| FileStatus other = (FileStatus)o; |
| return this.getPath().equals(other.getPath()); |
| } |
| |
| /** |
| * Returns a hash code value for the object, which is defined as |
| * the hash code of the path name. |
| * |
| * @return a hash code value for the path name. |
| */ |
| @Override |
| public int hashCode() { |
| return getPath().hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(getClass().getSimpleName()) |
| .append("{") |
| .append("path=" + path) |
| .append("; isDirectory=" + isdir); |
| if(!isDirectory()){ |
| sb.append("; length=" + length) |
| .append("; replication=" + block_replication) |
| .append("; blocksize=" + blocksize); |
| } |
| sb.append("; modification_time=" + modification_time) |
| .append("; access_time=" + access_time) |
| .append("; owner=" + owner) |
| .append("; group=" + group) |
| .append("; permission=" + permission) |
| .append("; isSymlink=" + isSymlink()); |
| if(isSymlink()) { |
| try { |
| sb.append("; symlink=" + getSymlink()); |
| } catch (IOException e) { |
| throw new RuntimeException("Unexpected exception", e); |
| } |
| } |
| sb.append("; hasAcl=" + hasAcl()) |
| .append("; isEncrypted=" + isEncrypted()) |
| .append("; isErasureCoded=" + isErasureCoded()) |
| .append("}"); |
| return sb.toString(); |
| } |
| |
| /** |
| * Read instance encoded as protobuf from stream. |
| * @param in Input stream |
| * @see PBHelper#convert(FileStatus) |
| * @deprecated Use the {@link PBHelper} and protobuf serialization directly. |
| */ |
| @Override |
| @Deprecated |
| public void readFields(DataInput in) throws IOException { |
| int size = in.readInt(); |
| if (size < 0) { |
| throw new IOException("Can't read FileStatusProto with negative " + |
| "size of " + size); |
| } |
| byte[] buf = new byte[size]; |
| in.readFully(buf); |
| FileStatusProto proto = FileStatusProto.parseFrom(buf); |
| FileStatus other = PBHelper.convert(proto); |
| isdir = other.isDirectory(); |
| length = other.getLen(); |
| block_replication = other.getReplication(); |
| blocksize = other.getBlockSize(); |
| modification_time = other.getModificationTime(); |
| access_time = other.getAccessTime(); |
| setPermission(other.getPermission()); |
| setOwner(other.getOwner()); |
| setGroup(other.getGroup()); |
| setSymlink((other.isSymlink() ? other.getSymlink() : null)); |
| setPath(other.getPath()); |
| attr = attributes(other.hasAcl(), other.isEncrypted(), |
| other.isErasureCoded(), other.isSnapshotEnabled()); |
| assert !(isDirectory() && isSymlink()) : "A directory cannot be a symlink"; |
| } |
| |
| /** |
| * Write instance encoded as protobuf to stream. |
| * @param out Output stream |
| * @see PBHelper#convert(FileStatus) |
| * @deprecated Use the {@link PBHelper} and protobuf serialization directly. |
| */ |
| @Override |
| @Deprecated |
| public void write(DataOutput out) throws IOException { |
| FileStatusProto proto = PBHelper.convert(this); |
| int size = proto.getSerializedSize(); |
| out.writeInt(size); |
| out.write(proto.toByteArray()); |
| } |
| |
| @Override |
| public void validateObject() throws InvalidObjectException { |
| if (null == path) { |
| throw new InvalidObjectException("No Path in deserialized FileStatus"); |
| } |
| if (null == isdir) { |
| throw new InvalidObjectException("No type in deserialized FileStatus"); |
| } |
| } |
| |
| |
| |
| } |