NfsFileBase.java
/**
* Copyright 2016-2018 Dell Inc. or its subsidiaries. All rights reserved.
*
* <p>Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file
* except in compliance with the License. A copy of the License is located at
*
* <p>http://www.apache.org/licenses/LICENSE-2.0.txt
*
* <p>or in the "license" file accompanying this file. This file 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.emc.ecs.nfsclient.nfs.io;
import com.emc.ecs.nfsclient.nfs.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Basic implementation of NfsFile. Subclasses need only implement constructors and newChildFile().
*
* @author seibed
*/
public abstract class NfsFileBase<N extends Nfs<F>, F extends NfsFile<N, F>>
implements NfsFile<N, F> {
private static final Logger LOG = LoggerFactory.getLogger(NfsFileBase.class);
/** The full network path to the file. */
private String _absolutePath;
/** The real backing file, in case the file is a symbolic link. */
private F _backingFile;
/** is it? */
private boolean _isRootFile = false;
/** file handle for NFS calls */
private byte[] _fileHandle;
/** The short name of the file, starting from the parent path. */
private String _name;
/** The supporting NFS client. */
private final N _nfs;
/** The full parent path of the file, starting with the mount point. */
private String _parent;
/** The parent file, stored to reduce lookup overhead */
private F _parentFile;
/** The full path of the file, starting with the mount point. */
private String _path;
/**
* The basic constructor.
*
* @param nfs The supporting NFS client.
* @param path The full path of the file, starting with the mount point.
* @param linkTracker The tracker to use. This must be passed so that monitoring is continued
* until the link resolves to a file that is not a symbolic link.
*/
public NfsFileBase(N nfs, String path, LinkTracker<N, F> linkTracker) throws IOException {
if (nfs == null) {
throw new IllegalArgumentException("Nfs instance can not be null");
}
_nfs = nfs;
_parent = makeParentPath(path);
F parent = isRootPath(path) ? null : newFile(_parent, linkTracker);
setParentFileAndName(parent, makeName(path), linkTracker);
}
/**
* The most efficient constructor if the parent file already exists.
*
* @param parentFile The parent file, stored to reduce lookup overhead
* @param childName The short name of the file, starting from the parent path.
*/
public NfsFileBase(F parentFile, String childName) throws IOException {
_nfs = parentFile.getNfs();
setParentFileAndName(parentFile, childName, null);
}
/** Returns everything up to and including the first separator character before the final name. */
public static String makeParentPath(String path) {
int firstParentSeparatorIndex = getParentSeparatorIndex(path);
while ((firstParentSeparatorIndex > 0)
&& (separatorChar == path.charAt(firstParentSeparatorIndex))) {
--firstParentSeparatorIndex;
}
int lengthOfParentName = (firstParentSeparatorIndex == 0) ? 1 : (firstParentSeparatorIndex + 2);
return path.substring(0, lengthOfParentName);
}
/** Returns everything after the last separator character. */
public static String makeName(String path) {
int nameStartIndex = getParentSeparatorIndex(path) + 1;
int endOfNameIndex = path.indexOf(separatorChar, nameStartIndex);
if (endOfNameIndex < 0) {
endOfNameIndex = path.length();
}
return path.substring(nameStartIndex, endOfNameIndex);
}
/** Returns the full child path. */
public static String makeChildPath(String parent, String child) {
StringBuilder stringBuilder = new StringBuilder(parent.replaceAll("/+", separator));
if (!parent.endsWith(separator)) {
stringBuilder.append(separatorChar);
}
return stringBuilder.append(child).toString();
}
/**
* Returns the separator index.
*
* @return the separator index
*/
private static int getParentSeparatorIndex(String path) {
int firstTerminalSeparatorIndex = path.length() - 1;
while ((firstTerminalSeparatorIndex > 0)
&& (separatorChar == path.charAt(firstTerminalSeparatorIndex))) {
--firstTerminalSeparatorIndex;
}
return path.lastIndexOf(separatorChar, firstTerminalSeparatorIndex);
}
/** {@inheritDoc} */
@Override
public boolean canDelete() throws IOException {
return canAccess(Nfs.ACCESS3_DELETE);
}
/** {@inheritDoc} */
@Override
public boolean canExecute() throws IOException {
return canAccess(Nfs.ACCESS3_EXECUTE);
}
/** {@inheritDoc} */
@Override
public boolean canExtend() throws IOException {
return canAccess(Nfs.ACCESS3_EXTEND);
}
/** {@inheritDoc} */
@Override
public boolean canLookup() throws IOException {
return canAccess(Nfs.ACCESS3_LOOKUP);
}
/** {@inheritDoc} */
@Override
public boolean canModify() throws IOException {
return canAccess(Nfs.ACCESS3_MODIFY);
}
/** {@inheritDoc} */
@Override
public boolean canRead() throws IOException {
return canAccess(Nfs.ACCESS3_READ);
}
/** {@inheritDoc} */
@Override
public final int compareTo(F o) {
if (o == null) {
return 1;
}
return getAbsolutePath().compareTo(o.getAbsolutePath());
}
/** {@inheritDoc} */
@Override
// Known NFS file types are canonical enum-style instances; identity is intentional.
@SuppressWarnings("ReferenceEquality")
public boolean createNewFile() throws IOException {
try {
create(NfsCreateMode.GUARDED, new NfsSetAttributes(), null);
} catch (NfsException e) {
if (e.getStatus() == NfsStatus.NFS3ERR_EXIST) {
return false;
}
throw e;
}
return true;
}
/** {@inheritDoc} */
@Override
// Known NFS file types are canonical enum-style instances; identity is intentional.
@SuppressWarnings("ReferenceEquality")
public void delete() throws IOException {
if (isDirectory()) {
rmdir();
} else {
remove();
}
}
/** {@inheritDoc} */
@Override
public final boolean equals(Object obj) {
if (!(obj instanceof NfsFile)) {
return false;
}
return getAbsolutePath().equals(((NfsFile<?, ?>) obj).getAbsolutePath());
}
/** {@inheritDoc} */
@Override
public boolean exists() throws IOException {
boolean exists = false;
try {
// force lookup again
setFileHandle(null);
exists = (getFileHandle() != null);
} catch (FileNotFoundException e) {
// do nothing
}
return exists;
}
/** {@inheritDoc} */
@Override
public F followLinks() throws IOException {
return followLinks(null);
}
/** {@inheritDoc} */
@Override
// Known NFS file types are canonical enum-style instances; identity is intentional.
@SuppressWarnings({"unchecked", "ReferenceEquality"})
public F followLinks(LinkTracker<N, F> linkTracker) throws IOException {
if (_backingFile == null) {
F backingFile = (F) this;
NfsGetAttributes attributes = safeGetAttributes(backingFile);
while (NfsType.NFS_LNK == attributes.getType()) {
if (linkTracker == null) {
linkTracker = new LinkTracker<N, F>();
}
F newBackingFile = (F) linkTracker.addLink(backingFile.getPath());
if (newBackingFile != null) {
backingFile = newBackingFile;
} else {
backingFile = followLink(backingFile.readlink().getData(), linkTracker);
}
attributes = safeGetAttributes(backingFile);
}
_backingFile = backingFile;
if (linkTracker != null) {
linkTracker.addResolvedPath(getPath(), _backingFile);
}
}
return _backingFile;
}
/**
* Returns the attributes, or empty attributes if they cannot be read.
*
* @param file The file for which the attributes are wanted.
* @return The attributes, or empty attributes if they cannot be read.
*/
private NfsGetAttributes safeGetAttributes(F file) {
if (file != null) {
try {
return file.getAttributes();
} catch (Exception e) {
LOG.debug(
"Cannot read NFS attributes while resolving a link; using empty attributes: file={}",
file.getAbsolutePath(),
e);
}
}
return new NfsGetAttributes();
}
/**
* Returns the file (which may itself be a link) corresponding to that path.
*
* @param target The symbolic link data
* @param linkTracker The tracker to use. This must be passed so that monitoring is continued
* until the link resolves to a file that is not a symbolic link.
* @return The file (which may itself be a link) corresponding to that path.
* @throws IOException if the target is blank or not in the mount point.
*/
private F followLink(String target, LinkTracker<N, F> linkTracker) throws IOException {
if (StringUtils.isBlank(target)) {
throw new IOException("blank link target");
}
if (!target.contains(separator)) {
return getParentFile().newChildFile(target);
} else {
String path;
if (target.startsWith(NfsFile.separator)) {
if (!target.startsWith(getNfs().getExportedPath())) {
throw new IOException("unreachable link target: " + target);
} else {
path = target.substring(getNfs().getExportedPath().length());
if (!path.startsWith(separator)) {
path = separator + path;
}
}
} else {
String parentPath = getParentFile().getPath();
if (parentPath.endsWith(separator)) {
path = parentPath + target;
} else {
path = parentPath + separator + target;
}
}
return newFile(path, linkTracker);
}
}
/** {@inheritDoc} */
@Override
public final String getAbsolutePath() {
return _absolutePath;
}
/** {@inheritDoc} */
@Override
public long getAccess(long accessToCheck) throws IOException {
return access(accessToCheck).getAccess();
}
/** {@inheritDoc} */
@Override
public NfsGetAttributes getAttributes() throws IOException {
return getNfs().wrapped_getAttr(getNfs().makeGetAttrRequest(getFileHandle())).getAttributes();
}
/** {@inheritDoc} */
@Override
public F getChildFile(String childName) throws IOException {
return newChildFile(childName);
}
/** {@inheritDoc} */
@Override
public byte[] getFileHandle() throws IOException {
if (_isRootFile) {
return getNfs().getRootFileHandle();
}
if (_fileHandle == null) {
setFileHandle();
}
return (_fileHandle == null) ? null : _fileHandle.clone();
}
/** {@inheritDoc} */
@Override
public long getFreeSpace() throws IOException {
return fsstat().getFsStat().fbytes;
}
/** {@inheritDoc} */
@Override
public long getMode() throws IOException {
return getAttributes().getMode();
}
/** {@inheritDoc} */
@Override
public final String getName() {
return _name;
}
/** {@inheritDoc} */
@Override
public final N getNfs() {
return _nfs;
}
/** {@inheritDoc} */
@Override
public final String getParent() {
return _parent;
}
/** {@inheritDoc} */
@Override
public F getParentFile() {
return _parentFile;
}
/** {@inheritDoc} */
@Override
public final String getPath() {
return _path;
}
/** {@inheritDoc} */
@Override
public long getTotalSpace() throws IOException {
return fsstat().getFsStat().tbytes;
}
/** {@inheritDoc} */
@Override
public long getUsableSpace() throws IOException {
return fsstat().getFsStat().bytes;
}
/** {@inheritDoc} */
@Override
public final int hashCode() {
return getAbsolutePath().hashCode();
}
/** {@inheritDoc} */
@Override
// Known NFS file types are canonical enum-style instances; identity is intentional.
@SuppressWarnings("ReferenceEquality")
public boolean isDirectory() throws IOException {
return getAttributes().getType() == NfsType.NFS_DIR;
}
/** {@inheritDoc} */
@Override
// Known NFS file types are canonical enum-style instances; identity is intentional.
@SuppressWarnings("ReferenceEquality")
public boolean isFile() throws IOException {
return getAttributes().getType() == NfsType.NFS_REG;
}
/** {@inheritDoc} */
@Override
public boolean isRootFile() throws IOException {
return _isRootFile;
}
/** {@inheritDoc} */
@Override
public long lastModified() throws IOException {
return getAttributes().getMtime().getTimeInMillis();
}
/** {@inheritDoc} */
@Override
public long length() {
try {
return lengthEx();
} catch (IOException e) {
LOG.debug("Cannot determine NFS file length; returning 0: file={}", getAbsolutePath(), e);
return 0;
}
}
/** {@inheritDoc} */
@Override
public long lengthEx() throws IOException {
return getAttributes().getSize();
}
/** {@inheritDoc} */
@Override
public List<String> list() throws IOException {
boolean eof = false;
long cookie = 0;
long cookieverf = 0;
final int count = 8 * 1024;
List<NfsDirectoryEntry> entries = new ArrayList<NfsDirectoryEntry>(32);
do {
NfsReaddirResponse response = readdir(cookie, cookieverf, count, entries);
eof = response.isEof();
cookie = response.getCookie();
cookieverf = response.getCookieverf();
} while (!eof);
List<String> children = new ArrayList<String>();
for (NfsDirectoryEntry entry : entries) {
if (!(".".equals(entry.getFileName()) || "..".equals(entry.getFileName()))) {
children.add(entry.getFileName());
}
}
return children;
}
/** {@inheritDoc} */
@Override
public List<String> list(NfsFilenameFilter filter) throws IOException {
List<String> children = list();
if ((children == null) || (children.size() == 0) || (filter == null)) {
return children;
}
List<String> filteredChildren = new ArrayList<String>();
for (String child : children) {
if (filter.accept(this, child)) {
filteredChildren.add(child);
}
}
return filteredChildren;
}
/** {@inheritDoc} */
@Override
public List<F> listFiles() throws IOException {
return getChildFiles(list());
}
/** {@inheritDoc} */
@Override
public List<F> listFiles(NfsFilenameFilter filter) throws IOException {
return getChildFiles(list(filter));
}
/** {@inheritDoc} */
@Override
public List<F> listFiles(NfsFileFilter filter) throws IOException {
List<F> childFiles = listFiles();
if ((childFiles == null) || (childFiles.size() == 0) || (filter == null)) {
return childFiles;
}
List<F> filteredFiles = new ArrayList<F>();
for (F childFile : childFiles) {
if (filter.accept(childFile)) {
filteredFiles.add(childFile);
}
}
return filteredFiles;
}
/** {@inheritDoc} */
@Override
public void mkdir() throws IOException {
mkdir(new NfsSetAttributes());
}
/** {@inheritDoc} */
@Override
public void mkdirs() throws IOException {
if (!getParent().equals(separator) && !getParentFile().exists()) {
getParentFile().mkdirs();
}
mkdir();
}
/** {@inheritDoc} */
@Override
public boolean renameTo(F destination) throws IOException {
return rename(destination).stateIsOk();
}
/** {@inheritDoc} */
@Override
public void setLastModified(long millis) throws IOException {
setAttributes(
new NfsSetAttributes(null, null, null, NfsTime.DO_NOT_CHANGE, new NfsTime(millis)));
}
/** {@inheritDoc} */
@Override
public final String toString() {
return getAbsolutePath();
}
/** {@inheritDoc} */
@Override
public void setAttributes(NfsSetAttributes nfsSetAttr) throws IOException {
setattr(nfsSetAttr, null);
}
/** {@inheritDoc} */
@Override
public void setMode(long mode) throws IOException {
setAttributes(
new NfsSetAttributes(
Long.valueOf(mode), null, null, NfsTime.DO_NOT_CHANGE, NfsTime.DO_NOT_CHANGE));
}
/** {@inheritDoc} */
@Override
public NfsGetAttrResponse getattr() throws IOException {
return getNfs().wrapped_getAttr(makeGetAttrRequest());
}
/** {@inheritDoc} */
@Override
public NfsGetAttrRequest makeGetAttrRequest() throws IOException {
return getNfs().makeGetAttrRequest(getFileHandle());
}
/** {@inheritDoc} */
@Override
public NfsSetAttrResponse setattr(NfsSetAttributes attributes, NfsTime guardTime)
throws IOException {
return getNfs().wrapped_setAttr(makeSetAttrRequest(attributes, guardTime));
}
/** {@inheritDoc} */
@Override
public NfsSetAttrRequest makeSetAttrRequest(NfsSetAttributes attributes, NfsTime guardTime)
throws IOException {
return getNfs().makeSetAttrRequest(getFileHandle(), attributes, guardTime);
}
/** {@inheritDoc} */
@Override
public NfsLookupResponse lookup() throws IOException {
return getNfs().wrapped_getLookup(makeLookupRequest());
}
/** {@inheritDoc} */
@Override
public NfsLookupRequest makeLookupRequest() throws IOException {
return getNfs().makeLookupRequest(getParentFile().getFileHandle(), getName());
}
/** {@inheritDoc} */
@Override
public NfsAccessResponse access(long accessToCheck) throws IOException {
return getNfs().wrapped_getAccess(makeAccessRequest(accessToCheck));
}
/** {@inheritDoc} */
@Override
public NfsAccessRequest makeAccessRequest(long accessToCheck) throws IOException {
return getNfs().makeAccessRequest(getFileHandle(), accessToCheck);
}
/** {@inheritDoc} */
@Override
public NfsReadlinkResponse readlink() throws IOException {
return getNfs().wrapped_getReadlink(makeReadlinkRequest());
}
/** {@inheritDoc} */
@Override
public NfsReadlinkRequest makeReadlinkRequest() throws IOException {
return getNfs().makeReadlinkRequest(getFileHandle());
}
/** {@inheritDoc} */
@Override
public NfsReadResponse read(long offset, int size, byte[] bytes, int position)
throws IOException {
return getNfs().wrapped_getRead(makeReadRequest(offset, size), bytes, position);
}
/** {@inheritDoc} */
@Override
public NfsReadRequest makeReadRequest(long offset, int size) throws IOException {
return getNfs().makeReadRequest(getFileHandle(), offset, size);
}
/** {@inheritDoc} */
@Override
public NfsWriteResponse write(long offset, List<ByteBuffer> payload, int syncType)
throws IOException {
return getNfs().wrapped_sendWrite(makeWriteRequest(offset, payload, syncType));
}
/** {@inheritDoc} */
@Override
public NfsWriteResponse write(long offset, List<ByteBuffer> payload, int syncType, Long verifier)
throws IOException {
return getNfs().wrapped_sendWrite(makeWriteRequest(offset, payload, syncType), verifier);
}
/** {@inheritDoc} */
@Override
public NfsWriteRequest makeWriteRequest(long offset, List<ByteBuffer> payload, int syncType)
throws IOException {
return getNfs().makeWriteRequest(getFileHandle(), offset, payload, syncType);
}
/** {@inheritDoc} */
@Override
public NfsCreateResponse create(
NfsCreateMode createMode, NfsSetAttributes attributes, byte[] verifier) throws IOException {
NfsCreateResponse response =
getNfs()
.wrapped_sendCreate(
getNfs()
.makeCreateRequest(
createMode,
getParentFile().getFileHandle(),
getName(),
attributes,
verifier));
setFileHandle(response.getFileHandle());
return response;
}
/** {@inheritDoc} */
@Override
public NfsCreateRequest makeCreateRequest(
NfsCreateMode createMode, NfsSetAttributes attributes, byte[] verifier) throws IOException {
return getNfs()
.makeCreateRequest(
createMode, getParentFile().getFileHandle(), getName(), attributes, verifier);
}
/** {@inheritDoc} */
@Override
public NfsMkdirResponse mkdir(NfsSetAttributes attributes) throws IOException {
NfsMkdirResponse response = getNfs().wrapped_sendMkdir(makeMkdirRequest(attributes));
setFileHandle(response.getFileHandle());
return response;
}
/** {@inheritDoc} */
@Override
public NfsMkdirRequest makeMkdirRequest(NfsSetAttributes attributes) throws IOException {
return getNfs().makeMkdirRequest(getParentFile().getFileHandle(), getName(), attributes);
}
/** {@inheritDoc} */
@Override
public NfsSymlinkResponse symlink(String symbolicLinkData, NfsSetAttributes attributes)
throws IOException {
NfsSymlinkResponse response =
getNfs().wrapped_sendSymlink(makeSymlinkRequest(symbolicLinkData, attributes));
setFileHandle(response.getFileHandle());
return response;
}
/** {@inheritDoc} */
@Override
public NfsSymlinkRequest makeSymlinkRequest(String symbolicLinkData, NfsSetAttributes attributes)
throws IOException {
return getNfs()
.makeSymlinkRequest(
symbolicLinkData, getParentFile().getFileHandle(), getName(), attributes);
}
/** {@inheritDoc} */
@Override
public NfsMknodResponse mknod(NfsType type, NfsSetAttributes attributes, long[] rdev)
throws IOException {
NfsMknodResponse response =
getNfs().wrapped_sendMknod(makeMknodRequest(type, attributes, rdev));
setFileHandle(response.getFileHandle());
return response;
}
/** {@inheritDoc} */
@Override
public NfsMknodRequest makeMknodRequest(NfsType type, NfsSetAttributes attributes, long[] rdev)
throws IOException {
return getNfs()
.makeMknodRequest(getParentFile().getFileHandle(), getName(), type, attributes, rdev);
}
/** {@inheritDoc} */
@Override
public NfsRemoveResponse remove() throws IOException {
NfsRemoveResponse response = getNfs().wrapped_sendRemove(makeRemoveRequest());
setFileHandle(null);
return response;
}
/** {@inheritDoc} */
@Override
public NfsRemoveRequest makeRemoveRequest() throws IOException {
return getNfs().makeRemoveRequest(getParentFile().getFileHandle(), getName());
}
/** {@inheritDoc} */
@Override
public NfsRmdirResponse rmdir() throws IOException {
NfsRmdirResponse response = getNfs().wrapped_sendRmdir(makeRmdirRequest());
setFileHandle(null);
return response;
}
/** {@inheritDoc} */
@Override
public NfsRmdirRequest makeRmdirRequest() throws IOException {
return getNfs().makeRmdirRequest(getParentFile().getFileHandle(), getName());
}
/** {@inheritDoc} */
@Override
public NfsRenameResponse rename(F toFile) throws IOException {
NfsRenameResponse response = getNfs().wrapped_sendRename(makeRenameRequest(toFile));
if (response.stateIsOk()) {
setPathFields(toFile);
}
return response;
}
/** {@inheritDoc} */
@Override
public NfsRenameRequest makeRenameRequest(F toFile) throws IOException {
return getNfs()
.makeRenameRequest(
getParentFile().getFileHandle(),
getName(),
toFile.getParentFile().getFileHandle(),
toFile.getName());
}
/** {@inheritDoc} */
@Override
public NfsLinkResponse link(F source) throws IOException {
NfsLinkResponse response = getNfs().wrapped_sendLink(makeLinkRequest(source));
setFileHandle(source.getFileHandle());
return response;
}
/** {@inheritDoc} */
@Override
public NfsLinkRequest makeLinkRequest(F source) throws IOException {
return getNfs()
.makeLinkRequest(source.getFileHandle(), getParentFile().getFileHandle(), getName());
}
/** {@inheritDoc} */
@Override
public NfsReaddirResponse readdir(long cookie, long cookieverf, int count) throws IOException {
return getNfs().wrapped_getReaddir(makeReaddirRequest(cookie, cookieverf, count));
}
/** {@inheritDoc} */
@Override
public NfsReaddirResponse readdir(
long cookie, long cookieverf, int count, List<NfsDirectoryEntry> entries) throws IOException {
return getNfs().wrapped_getReaddir(makeReaddirRequest(cookie, cookieverf, count), entries);
}
/** {@inheritDoc} */
@Override
public NfsReaddirRequest makeReaddirRequest(long cookie, long cookieverf, int count)
throws IOException {
return getNfs().makeReaddirRequest(followLinks().getFileHandle(), cookie, cookieverf, count);
}
/** {@inheritDoc} */
@Override
public NfsReaddirplusResponse readdirplus(
long cookie, long cookieverf, int dircount, int maxcount) throws IOException {
return getNfs()
.wrapped_getReaddirplus(makeReaddirplusRequest(cookie, cookieverf, dircount, maxcount));
}
/** {@inheritDoc} */
@Override
public NfsReaddirplusResponse readdirplus(
long cookie, long cookieverf, int dircount, int maxcount, List<NfsDirectoryPlusEntry> entries)
throws IOException {
return getNfs()
.wrapped_getReaddirplus(
makeReaddirplusRequest(cookie, cookieverf, dircount, maxcount), entries);
}
/** {@inheritDoc} */
@Override
public NfsReaddirplusRequest makeReaddirplusRequest(
long cookie, long cookieverf, int dircount, int maxcount) throws IOException {
return getNfs()
.makeReaddirplusRequest(
followLinks().getFileHandle(), cookie, cookieverf, dircount, maxcount);
}
/** {@inheritDoc} */
@Override
public NfsFsStatResponse fsstat() throws IOException {
return getNfs().wrapped_getFsStat(getNfs().makeFsStatRequest());
}
/** {@inheritDoc} */
@Override
public NfsFsStatRequest makeFsStatRequest() throws IOException {
return getNfs().makeFsStatRequest(getFileHandle());
}
/** {@inheritDoc} */
@Override
public NfsFsInfoResponse fsinfo() throws IOException {
return getNfs().wrapped_getFsInfo(getNfs().makeFsInfoRequest());
}
/** {@inheritDoc} */
@Override
public NfsFsInfoRequest makeFsInfoRequest() throws IOException {
return getNfs().makeFsInfoRequest(getFileHandle());
}
/** {@inheritDoc} */
@Override
public NfsPathconfResponse pathconf() throws IOException {
return getNfs().wrapped_getPathconf(makePathconfRequest());
}
/** {@inheritDoc} */
@Override
public NfsPathconfRequest makePathconfRequest() throws IOException {
return getNfs().makePathconfRequest(getFileHandle());
}
/** {@inheritDoc} */
@Override
public NfsCommitResponse commit(long offsetToCommit, int dataSizeToCommit) throws IOException {
return getNfs().wrapped_sendCommit(makeCommitRequest(offsetToCommit, dataSizeToCommit));
}
/** {@inheritDoc} */
@Override
public NfsCommitRequest makeCommitRequest(long offsetToCommit, int dataSizeToCommit)
throws IOException {
return getNfs().makeCommitRequest(getFileHandle(), offsetToCommit, dataSizeToCommit);
}
/**
* Conversion method.
*
* @param childNames A list of child names.
* @return The list of child files, one for each name.
*/
protected List<F> getChildFiles(List<String> childNames) throws IOException {
if (childNames == null) {
return null;
}
List<F> childFiles = new ArrayList<F>(childNames.size());
for (String childName : childNames) {
childFiles.add(getChildFile(childName));
}
return childFiles;
}
/**
* Returns true if it is, false if it is not.
*
* @return true if it is, false if it is not
*/
private final boolean isRootPath(String path) {
return (path == null) || "".equals(path) || separator.equals(path);
}
/** Sets the current NFS file handle. */
protected final void setFileHandle(byte[] fileHandle) {
_fileHandle = fileHandle;
}
/**
* This creates a new file with arbitrary path.
*
* @param path the absolute path from the mount point.
* @return the new nfs file.
*/
protected final F newFile(String path) throws IOException {
return newFile(path, null);
}
/**
* This creates a new file with arbitrary path, using the given LinkTracker.
*
* @param path the absolute path from the mount point.
* @param linkTracker The tracker to use. This must be passed so that monitoring is continued
* until the link resolves to a file that is not a symbolic link.
* @return A new file corresponding to the path, created using the linkTracker
*/
protected abstract F newFile(String path, LinkTracker<N, F> linkTracker) throws IOException;
/**
* This method handles special cases, such as symbolic links in the parent directory, empty
* filenames, or the special names "." and "..". The algorithm required is simplified by the fact
* that special cases for the parent file are handled before this is called, as the path is always
* resolved from the bottom up. This means that the special cases have already been resolved for
* the parents and all supporting ancestors, so those possibilities need only be considered at the
* current level, eliminating any need for explicit recursive handling here.
*
* @param parentFile The original parent file. This may be changed for cases that require special
* handling, e.g., symbolic links, ".", "..", and empty names.
* @param name The original name. This may also be changed for cases that require special
* handling.
* @param linkTracker The tracker to use. This must be passed so that monitoring is continued
* until the link resolves to a file that is not a symbolic link.
* @throws IOException if links cannot be followed.
*/
private void setParentFileAndName(F parentFile, String name, LinkTracker<N, F> linkTracker)
throws IOException {
if (parentFile != null) {
parentFile = parentFile.followLinks(linkTracker);
if (StringUtils.isBlank(name) || ".".equals(name)) {
name = parentFile.getName();
parentFile = parentFile.getParentFile();
} else if ("..".equals(name)) {
parentFile = parentFile.getParentFile();
if (parentFile == null) {
name = "";
} else {
name = parentFile.getName();
parentFile = parentFile.getParentFile();
}
}
}
_parentFile = parentFile;
_name = name;
setPathFields();
}
/** Copies the path fields from another file. */
private void setPathFields() {
if (_parentFile == null) {
_parent = separator;
} else {
_parent = _parentFile.getPath();
if (!_parent.endsWith(separator)) {
_parent = _parent + separator;
}
if (StringUtils.isEmpty(_name)) {
_path = _parent;
_absolutePath = _parentFile.getAbsolutePath();
} else {
_path = _parent + separator + _name;
_absolutePath = _parentFile.getAbsolutePath() + separator + _name;
}
}
_isRootFile = (_parentFile == null);
String absolutePathBase =
(_parentFile != null)
? _parentFile.getAbsolutePath()
: (getNfs().getServer() + ":" + getNfs().getExportedPath());
if (!absolutePathBase.endsWith(separator)) {
absolutePathBase = absolutePathBase + separator;
}
if (StringUtils.isEmpty(_name)) {
_path = _parent;
_absolutePath = absolutePathBase;
} else {
_path = _parent + _name;
_absolutePath = absolutePathBase + _name;
}
}
/** Copies the path fields from another file. */
protected final void setPathFields(F toFile) {
_parentFile = toFile.getParentFile();
_parent = toFile.getParent();
_path = toFile.getPath();
_absolutePath = toFile.getAbsolutePath();
_name = toFile.getName();
}
/**
* Returns <code>true</code> if the access is allowed, <code>false</code> if it is not.
*
* @return <code>true</code> if the access is allowed, <code>false</code> if it is not
*/
private boolean canAccess(long accessToCheck) throws IOException {
return (accessToCheck & getAccess(accessToCheck)) != 0;
}
/** Set the file handle from the _path value */
private void setFileHandle() {
byte[] fileHandle = null;
if (_isRootFile) {
fileHandle = getNfs().getRootFileHandle();
} else {
try {
if (getParentFile().getFileHandle() != null) {
fileHandle = getNfs().wrapped_getLookup(makeLookupRequest()).getFileHandle();
}
} catch (IOException e) {
LOG.debug(
"Cannot look up NFS file handle; treating file as absent: file={}",
getAbsolutePath(),
e);
}
}
setFileHandle(fileHandle);
}
}