/* dCache - http://www.dcache.org/
*
* Copyright (C) 2014 - 2015 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.chimera.namespace;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import diskCacheV111.util.FsPath;
import diskCacheV111.util.PnfsId;
import org.dcache.acl.ACE;
import org.dcache.acl.ACL;
import org.dcache.acl.enums.RsType;
import org.dcache.chimera.ChimeraFsException;
import org.dcache.chimera.FileSystemProvider;
import org.dcache.chimera.FsInode;
import org.dcache.chimera.FsInodeType;
import org.dcache.chimera.StorageLocatable;
import org.dcache.chimera.UnixPermission;
import org.dcache.chimera.store.InodeStorageInformation;
import org.dcache.namespace.FileType;
import org.dcache.util.Checksum;
/**
* A Chimera inode extension that provides easy access to and caching of data
* associated with an inode.
*
* In the Chimera schema, the extended data isn't stored in the inode table
* and thus isn't accessible through the base class.
*/
public class ExtendedInode extends FsInode
{
private ImmutableMap<String,byte[]> tags;
private ImmutableList<Checksum> checksums;
private ImmutableList<StorageLocatable> locations;
private ImmutableMap<String, String> flags;
private ACL acl;
private HashMap<Integer, ExtendedInode> levels;
private InodeStorageInformation storageInfo;
private Optional<ExtendedInode> parent;
private ExtendedInode(ExtendedInode parent, FsInode inode)
{
this(parent.getFs(), inode);
this.parent = Optional.of(parent);
}
public ExtendedInode(FileSystemProvider fs, PnfsId id, FileSystemProvider.StatCacheOption option)
throws ChimeraFsException
{
this(fs, fs.id2inode(id.getId(), option));
}
public ExtendedInode(FileSystemProvider fs, FsInode inode)
{
super(fs, inode);
}
public ExtendedInode(FileSystemProvider fs, long id, FsInodeType type)
{
super(fs, id, type);
}
public ExtendedInode(FileSystemProvider fs, long id)
{
super(fs, id);
}
public ExtendedInode(FileSystemProvider fs, long id, int level)
{
super(fs, id, level);
}
public ExtendedInode(FileSystemProvider fs, long id, FsInodeType type, int level)
{
super(fs, id, type, level);
}
@Override
public ExtendedInode mkdir(String newDir) throws ChimeraFsException
{
return new ExtendedInode(this, super.mkdir(newDir));
}
@Override
public ExtendedInode mkdir(String name, int owner, int group, int mode) throws ChimeraFsException
{
return new ExtendedInode(this, super.mkdir(name, owner, group, mode));
}
@Override
public ExtendedInode mkdir(String name, int owner, int group, int mode, List<ACE> acl, Map<String, byte[]> tags)
throws ChimeraFsException
{
return new ExtendedInode(this, super.mkdir(name, owner, group, mode, acl, tags));
}
@Override
public ExtendedInode create(String name, int uid, int gid, int mode) throws ChimeraFsException
{
return new ExtendedInode(this, super.create(name, uid, gid, mode));
}
@Override
public ExtendedInode inodeOf(String name, FileSystemProvider.StatCacheOption stat) throws ChimeraFsException
{
return new ExtendedInode(this, super.inodeOf(name, stat));
}
@Override
public ExtendedInode getParent()
{
if (parent == null) {
FsInode actualParent = super.getParent();
parent = Optional.fromNullable(actualParent != null ? new ExtendedInode(getFs(), actualParent) : null);
}
return parent.orNull();
}
public PnfsId getPnfsId() throws ChimeraFsException
{
return new PnfsId(getId());
}
public ImmutableMap<String,byte[]> getTags() throws ChimeraFsException
{
if (tags == null) {
tags = ImmutableMap.copyOf(_fs.getAllTags(this));
}
return tags;
}
public ImmutableList<String> getTag(String tag)
{
try {
byte[] data = getTags().get(tag);
if (data == null || data.length == 0) {
return ImmutableList.of();
}
return ByteSource.wrap(data).asCharSource(Charsets.UTF_8).readLines();
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
public ImmutableCollection<Checksum> getChecksums() throws ChimeraFsException
{
if (checksums == null) {
checksums = ImmutableList.copyOf(_fs.getInodeChecksums(this));
}
return checksums;
}
public ImmutableList<String> getLocations(int type) throws ChimeraFsException
{
return ImmutableList.copyOf(
getLocations().stream().filter(l -> l.type() == type).map(StorageLocatable::location).iterator());
}
public ImmutableList<StorageLocatable> getLocations() throws ChimeraFsException
{
if (locations == null) {
locations = ImmutableList.copyOf(_fs.getInodeLocations(this));
}
return locations;
}
public ImmutableMap<String,String> getFlags() throws ChimeraFsException
{
if (flags == null) {
ImmutableMap.Builder<String,String> builder = ImmutableMap.builder();
ExtendedInode level2 = getLevel(2);
try {
ChimeraCacheInfo info = new ChimeraCacheInfo(level2);
for (Map.Entry<String,String> e: info.getFlags().entrySet()) {
builder.put(e.getKey(), e.getValue());
}
} catch (IOException e) {
throw new ChimeraFsException(e.getMessage(), e);
}
flags = builder.build();
}
return flags;
}
public ACL getAcl() throws ChimeraFsException
{
if (acl == null) {
RsType rsType = isDirectory() ? RsType.DIR : RsType.FILE;
acl = new ACL(rsType, _fs.getACL(this));
}
return acl;
}
public ExtendedInode getLevel(int level)
{
if (levels == null) {
levels = new HashMap<>();
}
ExtendedInode inode = levels.get(level);
if (inode == null) {
inode = new ExtendedInode(_fs, ino(), level);
levels.put(level, inode);
}
return inode;
}
public InodeStorageInformation getStorageInfo() throws ChimeraFsException
{
if (storageInfo == null) {
storageInfo = _fs.getStorageInfo(this);
}
return storageInfo;
}
public FsPath getPath() throws ChimeraFsException
{
return FsPath.create(_fs.inode2path(this));
}
public FileType getFileType() throws ChimeraFsException
{
switch (UnixPermission.getType(statCache().getMode())) {
case UnixPermission.S_IFREG:
return FileType.REGULAR;
case UnixPermission.S_IFDIR:
return FileType.DIR;
case UnixPermission.S_IFLNK:
return FileType.LINK;
default:
return FileType.SPECIAL;
}
}
}