|
@@ -46,6 +46,21 @@ import com.google.common.base.Preconditions;
|
|
*/
|
|
*/
|
|
public class INodeDirectory extends INodeWithAdditionalFields
|
|
public class INodeDirectory extends INodeWithAdditionalFields
|
|
implements INodeDirectoryAttributes {
|
|
implements INodeDirectoryAttributes {
|
|
|
|
+ /** Directory related features such as quota and snapshots. */
|
|
|
|
+ public static abstract class Feature implements INode.Feature<Feature> {
|
|
|
|
+ private Feature nextFeature;
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Feature getNextFeature() {
|
|
|
|
+ return nextFeature;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void setNextFeature(Feature next) {
|
|
|
|
+ this.nextFeature = next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
/** Cast INode to INodeDirectory. */
|
|
/** Cast INode to INodeDirectory. */
|
|
public static INodeDirectory valueOf(INode inode, Object path
|
|
public static INodeDirectory valueOf(INode inode, Object path
|
|
) throws FileNotFoundException, PathIsNotDirectoryException {
|
|
) throws FileNotFoundException, PathIsNotDirectoryException {
|
|
@@ -63,6 +78,9 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
final static byte[] ROOT_NAME = DFSUtil.string2Bytes("");
|
|
final static byte[] ROOT_NAME = DFSUtil.string2Bytes("");
|
|
|
|
|
|
private List<INode> children = null;
|
|
private List<INode> children = null;
|
|
|
|
+
|
|
|
|
+ /** A linked list of {@link Feature}s. */
|
|
|
|
+ private Feature headFeature = null;
|
|
|
|
|
|
/** constructor */
|
|
/** constructor */
|
|
public INodeDirectory(long id, byte[] name, PermissionStatus permissions,
|
|
public INodeDirectory(long id, byte[] name, PermissionStatus permissions,
|
|
@@ -76,7 +94,7 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
* @param adopt Indicate whether or not need to set the parent field of child
|
|
* @param adopt Indicate whether or not need to set the parent field of child
|
|
* INodes to the new node
|
|
* INodes to the new node
|
|
*/
|
|
*/
|
|
- public INodeDirectory(INodeDirectory other, boolean adopt) {
|
|
|
|
|
|
+ public INodeDirectory(INodeDirectory other, boolean adopt, boolean copyFeatures) {
|
|
super(other);
|
|
super(other);
|
|
this.children = other.children;
|
|
this.children = other.children;
|
|
if (adopt && this.children != null) {
|
|
if (adopt && this.children != null) {
|
|
@@ -84,6 +102,9 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
child.setParent(this);
|
|
child.setParent(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ if (copyFeatures) {
|
|
|
|
+ this.headFeature = other.headFeature;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/** @return true unconditionally. */
|
|
/** @return true unconditionally. */
|
|
@@ -103,6 +124,73 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ void setQuota(long nsQuota, long dsQuota) {
|
|
|
|
+ DirectoryWithQuotaFeature quota = getDirectoryWithQuotaFeature();
|
|
|
|
+ if (quota != null) {
|
|
|
|
+ // already has quota; so set the quota to the new values
|
|
|
|
+ quota.setQuota(nsQuota, dsQuota);
|
|
|
|
+ if (!isQuotaSet() && !isRoot()) {
|
|
|
|
+ removeFeature(quota);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ final Quota.Counts c = computeQuotaUsage();
|
|
|
|
+ quota = addDirectoryWithQuotaFeature(nsQuota, dsQuota);
|
|
|
|
+ quota.setSpaceConsumed(c.get(Quota.NAMESPACE), c.get(Quota.DISKSPACE));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Quota.Counts getQuotaCounts() {
|
|
|
|
+ final DirectoryWithQuotaFeature q = getDirectoryWithQuotaFeature();
|
|
|
|
+ return q != null? q.getQuota(): super.getQuotaCounts();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void addSpaceConsumed(long nsDelta, long dsDelta, boolean verify)
|
|
|
|
+ throws QuotaExceededException {
|
|
|
|
+ final DirectoryWithQuotaFeature q = getDirectoryWithQuotaFeature();
|
|
|
|
+ if (q != null) {
|
|
|
|
+ q.addSpaceConsumed(this, nsDelta, dsDelta, verify);
|
|
|
|
+ } else {
|
|
|
|
+ addSpaceConsumed2Parent(nsDelta, dsDelta, verify);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * If the directory contains a {@link DirectoryWithQuotaFeature}, return it;
|
|
|
|
+ * otherwise, return null.
|
|
|
|
+ */
|
|
|
|
+ public final DirectoryWithQuotaFeature getDirectoryWithQuotaFeature() {
|
|
|
|
+ for(Feature f = headFeature; f != null; f = f.nextFeature) {
|
|
|
|
+ if (f instanceof DirectoryWithQuotaFeature) {
|
|
|
|
+ return (DirectoryWithQuotaFeature)f;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /** Is this directory with quota? */
|
|
|
|
+ final boolean isWithQuota() {
|
|
|
|
+ return getDirectoryWithQuotaFeature() != null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DirectoryWithQuotaFeature addDirectoryWithQuotaFeature(
|
|
|
|
+ long nsQuota, long dsQuota) {
|
|
|
|
+ Preconditions.checkState(!isWithQuota(), "Directory is already with quota");
|
|
|
|
+ final DirectoryWithQuotaFeature quota = new DirectoryWithQuotaFeature(
|
|
|
|
+ nsQuota, dsQuota);
|
|
|
|
+ addFeature(quota);
|
|
|
|
+ return quota;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void addFeature(Feature f) {
|
|
|
|
+ headFeature = INode.Feature.Util.addFeature(f, headFeature);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void removeFeature(Feature f) {
|
|
|
|
+ headFeature = INode.Feature.Util.removeFeature(f, headFeature);
|
|
|
|
+ }
|
|
|
|
+
|
|
private int searchChildren(byte[] name) {
|
|
private int searchChildren(byte[] name) {
|
|
return children == null? -1: Collections.binarySearch(children, name);
|
|
return children == null? -1: Collections.binarySearch(children, name);
|
|
}
|
|
}
|
|
@@ -142,27 +230,6 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- /**
|
|
|
|
- * Replace itself with {@link INodeDirectoryWithQuota} or
|
|
|
|
- * {@link INodeDirectoryWithSnapshot} depending on the latest snapshot.
|
|
|
|
- */
|
|
|
|
- INodeDirectoryWithQuota replaceSelf4Quota(final Snapshot latest,
|
|
|
|
- final long nsQuota, final long dsQuota, final INodeMap inodeMap)
|
|
|
|
- throws QuotaExceededException {
|
|
|
|
- Preconditions.checkState(!(this instanceof INodeDirectoryWithQuota),
|
|
|
|
- "this is already an INodeDirectoryWithQuota, this=%s", this);
|
|
|
|
-
|
|
|
|
- if (!this.isInLatestSnapshot(latest)) {
|
|
|
|
- final INodeDirectoryWithQuota q = new INodeDirectoryWithQuota(
|
|
|
|
- this, true, nsQuota, dsQuota);
|
|
|
|
- replaceSelf(q, inodeMap);
|
|
|
|
- return q;
|
|
|
|
- } else {
|
|
|
|
- final INodeDirectoryWithSnapshot s = new INodeDirectoryWithSnapshot(this);
|
|
|
|
- s.setQuota(nsQuota, dsQuota);
|
|
|
|
- return replaceSelf(s, inodeMap).saveSelf2Snapshot(latest, this);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
/** Replace itself with an {@link INodeDirectorySnapshottable}. */
|
|
/** Replace itself with an {@link INodeDirectorySnapshottable}. */
|
|
public INodeDirectorySnapshottable replaceSelf4INodeDirectorySnapshottable(
|
|
public INodeDirectorySnapshottable replaceSelf4INodeDirectorySnapshottable(
|
|
Snapshot latest, final INodeMap inodeMap) throws QuotaExceededException {
|
|
Snapshot latest, final INodeMap inodeMap) throws QuotaExceededException {
|
|
@@ -183,7 +250,7 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
public INodeDirectory replaceSelf4INodeDirectory(final INodeMap inodeMap) {
|
|
public INodeDirectory replaceSelf4INodeDirectory(final INodeMap inodeMap) {
|
|
Preconditions.checkState(getClass() != INodeDirectory.class,
|
|
Preconditions.checkState(getClass() != INodeDirectory.class,
|
|
"the class is already INodeDirectory, this=%s", this);
|
|
"the class is already INodeDirectory, this=%s", this);
|
|
- return replaceSelf(new INodeDirectory(this, true), inodeMap);
|
|
|
|
|
|
+ return replaceSelf(new INodeDirectory(this, true, true), inodeMap);
|
|
}
|
|
}
|
|
|
|
|
|
/** Replace itself with the given directory. */
|
|
/** Replace itself with the given directory. */
|
|
@@ -439,6 +506,21 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
@Override
|
|
@Override
|
|
public Quota.Counts computeQuotaUsage(Quota.Counts counts, boolean useCache,
|
|
public Quota.Counts computeQuotaUsage(Quota.Counts counts, boolean useCache,
|
|
int lastSnapshotId) {
|
|
int lastSnapshotId) {
|
|
|
|
+ final DirectoryWithQuotaFeature q = getDirectoryWithQuotaFeature();
|
|
|
|
+ if (q != null) {
|
|
|
|
+ if (useCache && isQuotaSet()) {
|
|
|
|
+ q.addNamespaceDiskspace(counts);
|
|
|
|
+ } else {
|
|
|
|
+ computeDirectoryQuotaUsage(counts, false, lastSnapshotId);
|
|
|
|
+ }
|
|
|
|
+ return counts;
|
|
|
|
+ } else {
|
|
|
|
+ return computeDirectoryQuotaUsage(counts, useCache, lastSnapshotId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Quota.Counts computeDirectoryQuotaUsage(Quota.Counts counts, boolean useCache,
|
|
|
|
+ int lastSnapshotId) {
|
|
if (children != null) {
|
|
if (children != null) {
|
|
for (INode child : children) {
|
|
for (INode child : children) {
|
|
child.computeQuotaUsage(counts, useCache, lastSnapshotId);
|
|
child.computeQuotaUsage(counts, useCache, lastSnapshotId);
|
|
@@ -456,6 +538,16 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
@Override
|
|
@Override
|
|
public ContentSummaryComputationContext computeContentSummary(
|
|
public ContentSummaryComputationContext computeContentSummary(
|
|
ContentSummaryComputationContext summary) {
|
|
ContentSummaryComputationContext summary) {
|
|
|
|
+ final DirectoryWithQuotaFeature q = getDirectoryWithQuotaFeature();
|
|
|
|
+ if (q != null) {
|
|
|
|
+ return q.computeContentSummary(this, summary);
|
|
|
|
+ } else {
|
|
|
|
+ return computeDirectoryContentSummary(summary);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ContentSummaryComputationContext computeDirectoryContentSummary(
|
|
|
|
+ ContentSummaryComputationContext summary) {
|
|
ReadOnlyList<INode> childrenList = getChildrenList(null);
|
|
ReadOnlyList<INode> childrenList = getChildrenList(null);
|
|
// Explicit traversing is done to enable repositioning after relinquishing
|
|
// Explicit traversing is done to enable repositioning after relinquishing
|
|
// and reacquiring locks.
|
|
// and reacquiring locks.
|
|
@@ -570,7 +662,7 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
Quota.Counts counts = cleanSubtreeRecursively(snapshot, prior,
|
|
Quota.Counts counts = cleanSubtreeRecursively(snapshot, prior,
|
|
collectedBlocks, removedINodes, null, countDiffChange);
|
|
collectedBlocks, removedINodes, null, countDiffChange);
|
|
if (isQuotaSet()) {
|
|
if (isQuotaSet()) {
|
|
- ((INodeDirectoryWithQuota) this).addSpaceConsumed2Cache(
|
|
|
|
|
|
+ getDirectoryWithQuotaFeature().addSpaceConsumed2Cache(
|
|
-counts.get(Quota.NAMESPACE), -counts.get(Quota.DISKSPACE));
|
|
-counts.get(Quota.NAMESPACE), -counts.get(Quota.DISKSPACE));
|
|
}
|
|
}
|
|
return counts;
|
|
return counts;
|
|
@@ -606,8 +698,9 @@ public class INodeDirectory extends INodeWithAdditionalFields
|
|
final Snapshot snapshot) {
|
|
final Snapshot snapshot) {
|
|
super.dumpTreeRecursively(out, prefix, snapshot);
|
|
super.dumpTreeRecursively(out, prefix, snapshot);
|
|
out.print(", childrenSize=" + getChildrenList(snapshot).size());
|
|
out.print(", childrenSize=" + getChildrenList(snapshot).size());
|
|
- if (this instanceof INodeDirectoryWithQuota) {
|
|
|
|
- out.print(((INodeDirectoryWithQuota)this).quotaString());
|
|
|
|
|
|
+ final DirectoryWithQuotaFeature q = getDirectoryWithQuotaFeature();
|
|
|
|
+ if (q != null) {
|
|
|
|
+ out.print(", " + q);
|
|
}
|
|
}
|
|
if (this instanceof Snapshot.Root) {
|
|
if (this instanceof Snapshot.Root) {
|
|
out.print(", snapshotId=" + snapshot.getId());
|
|
out.print(", snapshotId=" + snapshot.getId());
|