001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hdfs.server.namenode;
019
020import java.io.PrintWriter;
021import java.util.ArrayList;
022import java.util.Collections;
023import java.util.Comparator;
024import java.util.List;
025
026import org.apache.hadoop.fs.permission.FsPermission;
027import org.apache.hadoop.fs.permission.PermissionStatus;
028import org.apache.hadoop.hdfs.protocol.QuotaExceededException;
029import org.apache.hadoop.hdfs.server.blockmanagement.BlockStoragePolicySuite;
030import org.apache.hadoop.hdfs.server.namenode.snapshot.DirectoryWithSnapshotFeature;
031import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
032
033import com.google.common.base.Preconditions;
034import org.mortbay.log.Log;
035
036/**
037 * An anonymous reference to an inode.
038 *
039 * This class and its subclasses are used to support multiple access paths.
040 * A file/directory may have multiple access paths when it is stored in some
041 * snapshots and it is renamed/moved to other locations.
042 * 
043 * For example,
044 * (1) Suppose we have /abc/foo, say the inode of foo is inode(id=1000,name=foo)
045 * (2) create snapshot s0 for /abc
046 * (3) mv /abc/foo /xyz/bar, i.e. inode(id=1000,name=...) is renamed from "foo"
047 *     to "bar" and its parent becomes /xyz.
048 * 
049 * Then, /xyz/bar and /abc/.snapshot/s0/foo are two different access paths to
050 * the same inode, inode(id=1000,name=bar).
051 *
052 * With references, we have the following
053 * - /abc has a child ref(id=1001,name=foo).
054 * - /xyz has a child ref(id=1002) 
055 * - Both ref(id=1001,name=foo) and ref(id=1002) point to another reference,
056 *   ref(id=1003,count=2).
057 * - Finally, ref(id=1003,count=2) points to inode(id=1000,name=bar).
058 * 
059 * Note 1: For a reference without name, e.g. ref(id=1002), it uses the name
060 *         of the referred inode.
061 * Note 2: getParent() always returns the parent in the current state, e.g.
062 *         inode(id=1000,name=bar).getParent() returns /xyz but not /abc.
063 */
064public abstract class INodeReference extends INode {
065  /**
066   * Try to remove the given reference and then return the reference count.
067   * If the given inode is not a reference, return -1;
068   */
069  public static int tryRemoveReference(INode inode) {
070    if (!inode.isReference()) {
071      return -1;
072    }
073    return removeReference(inode.asReference());
074  }
075
076  /**
077   * Remove the given reference and then return the reference count.
078   * If the referred inode is not a WithCount, return -1;
079   */
080  private static int removeReference(INodeReference ref) {
081    final INode referred = ref.getReferredINode();
082    if (!(referred instanceof WithCount)) {
083      return -1;
084    }
085    
086    WithCount wc = (WithCount) referred;
087    wc.removeReference(ref);
088    return wc.getReferenceCount();
089  }
090
091  /**
092   * When destroying a reference node (WithName or DstReference), we call this
093   * method to identify the snapshot which is the latest snapshot before the
094   * reference node's creation. 
095   */
096  static int getPriorSnapshot(INodeReference ref) {
097    WithCount wc = (WithCount) ref.getReferredINode();
098    WithName wn = null;
099    if (ref instanceof DstReference) {
100      wn = wc.getLastWithName();
101    } else if (ref instanceof WithName) {
102      wn = wc.getPriorWithName((WithName) ref);
103    }
104    if (wn != null) {
105      INode referred = wc.getReferredINode();
106      if (referred.isFile() && referred.asFile().isWithSnapshot()) {
107        return referred.asFile().getDiffs().getPrior(wn.lastSnapshotId);
108      } else if (referred.isDirectory()) {
109        DirectoryWithSnapshotFeature sf = referred.asDirectory()
110            .getDirectoryWithSnapshotFeature();
111        if (sf != null) {
112          return sf.getDiffs().getPrior(wn.lastSnapshotId);
113        }
114      }
115    }
116    return Snapshot.NO_SNAPSHOT_ID;
117  }
118  
119  private INode referred;
120  
121  public INodeReference(INode parent, INode referred) {
122    super(parent);
123    this.referred = referred;
124  }
125
126  public final INode getReferredINode() {
127    return referred;
128  }
129
130  public final void setReferredINode(INode referred) {
131    this.referred = referred;
132  }
133  
134  @Override
135  public final boolean isReference() {
136    return true;
137  }
138  
139  @Override
140  public final INodeReference asReference() {
141    return this;
142  }
143
144  @Override
145  public final boolean isFile() {
146    return referred.isFile();
147  }
148  
149  @Override
150  public final INodeFile asFile() {
151    return referred.asFile();
152  }
153  
154  @Override
155  public final boolean isDirectory() {
156    return referred.isDirectory();
157  }
158  
159  @Override
160  public final INodeDirectory asDirectory() {
161    return referred.asDirectory();
162  }
163  
164  @Override
165  public final boolean isSymlink() {
166    return referred.isSymlink();
167  }
168  
169  @Override
170  public final INodeSymlink asSymlink() {
171    return referred.asSymlink();
172  }
173
174  @Override
175  public byte[] getLocalNameBytes() {
176    return referred.getLocalNameBytes();
177  }
178
179  @Override
180  public void setLocalName(byte[] name) {
181    referred.setLocalName(name);
182  }
183
184  @Override
185  public final long getId() {
186    return referred.getId();
187  }
188  
189  @Override
190  public final PermissionStatus getPermissionStatus(int snapshotId) {
191    return referred.getPermissionStatus(snapshotId);
192  }
193  
194  @Override
195  public final String getUserName(int snapshotId) {
196    return referred.getUserName(snapshotId);
197  }
198  
199  @Override
200  final void setUser(String user) {
201    referred.setUser(user);
202  }
203  
204  @Override
205  public final String getGroupName(int snapshotId) {
206    return referred.getGroupName(snapshotId);
207  }
208  
209  @Override
210  final void setGroup(String group) {
211    referred.setGroup(group);
212  }
213  
214  @Override
215  public final FsPermission getFsPermission(int snapshotId) {
216    return referred.getFsPermission(snapshotId);
217  }
218
219  @Override
220  final AclFeature getAclFeature(int snapshotId) {
221    return referred.getAclFeature(snapshotId);
222  }
223
224  @Override
225  final void addAclFeature(AclFeature aclFeature) {
226    referred.addAclFeature(aclFeature);
227  }
228
229  @Override
230  final void removeAclFeature() {
231    referred.removeAclFeature();
232  }
233  
234  @Override
235  final XAttrFeature getXAttrFeature(int snapshotId) {
236    return referred.getXAttrFeature(snapshotId);
237  }
238  
239  @Override
240  final void addXAttrFeature(XAttrFeature xAttrFeature) {
241    referred.addXAttrFeature(xAttrFeature);
242  }
243  
244  @Override
245  final void removeXAttrFeature() {
246    referred.removeXAttrFeature();
247  }
248
249  @Override
250  public final short getFsPermissionShort() {
251    return referred.getFsPermissionShort();
252  }
253  
254  @Override
255  void setPermission(FsPermission permission) {
256    referred.setPermission(permission);
257  }
258
259  @Override
260  public long getPermissionLong() {
261    return referred.getPermissionLong();
262  }
263
264  @Override
265  public final long getModificationTime(int snapshotId) {
266    return referred.getModificationTime(snapshotId);
267  }
268  
269  @Override
270  public final INode updateModificationTime(long mtime, int latestSnapshotId) {
271    return referred.updateModificationTime(mtime, latestSnapshotId);
272  }
273  
274  @Override
275  public final void setModificationTime(long modificationTime) {
276    referred.setModificationTime(modificationTime);
277  }
278  
279  @Override
280  public final long getAccessTime(int snapshotId) {
281    return referred.getAccessTime(snapshotId);
282  }
283  
284  @Override
285  public final void setAccessTime(long accessTime) {
286    referred.setAccessTime(accessTime);
287  }
288
289  @Override
290  public final byte getStoragePolicyID() {
291    return referred.getStoragePolicyID();
292  }
293
294  @Override
295  public final byte getLocalStoragePolicyID() {
296    return referred.getLocalStoragePolicyID();
297  }
298
299  @Override
300  final void recordModification(int latestSnapshotId) {
301    referred.recordModification(latestSnapshotId);
302  }
303
304  @Override // used by WithCount
305  public QuotaCounts cleanSubtree(BlockStoragePolicySuite bsps, int snapshot,
306      int prior, BlocksMapUpdateInfo collectedBlocks,
307      final List<INode> removedINodes) {
308    return referred.cleanSubtree(bsps, snapshot, prior, collectedBlocks,
309        removedINodes);
310  }
311
312  @Override // used by WithCount
313  public void destroyAndCollectBlocks(
314      BlockStoragePolicySuite bsps,
315      BlocksMapUpdateInfo collectedBlocks, final List<INode> removedINodes) {
316    if (removeReference(this) <= 0) {
317      referred.destroyAndCollectBlocks(bsps, collectedBlocks, removedINodes);
318    }
319  }
320
321  @Override
322  public ContentSummaryComputationContext computeContentSummary(
323      ContentSummaryComputationContext summary) {
324    return referred.computeContentSummary(summary);
325  }
326
327  @Override
328  public QuotaCounts computeQuotaUsage(
329    BlockStoragePolicySuite bsps, byte blockStoragePolicyId,
330    QuotaCounts counts, boolean useCache, int lastSnapshotId) {
331    return referred.computeQuotaUsage(bsps, blockStoragePolicyId, counts,
332        useCache, lastSnapshotId);
333  }
334
335  @Override
336  public final INodeAttributes getSnapshotINode(int snapshotId) {
337    return referred.getSnapshotINode(snapshotId);
338  }
339
340  @Override
341  public QuotaCounts getQuotaCounts() {
342    return referred.getQuotaCounts();
343  }
344
345  @Override
346  public final void clear() {
347    super.clear();
348    referred = null;
349  }
350
351  @Override
352  public void dumpTreeRecursively(PrintWriter out, StringBuilder prefix,
353      final int snapshot) {
354    super.dumpTreeRecursively(out, prefix, snapshot);
355    if (this instanceof DstReference) {
356      out.print(", dstSnapshotId=" + ((DstReference) this).dstSnapshotId);
357    }
358    if (this instanceof WithCount) {
359      out.print(", count=" + ((WithCount)this).getReferenceCount());
360    }
361    out.println();
362    
363    final StringBuilder b = new StringBuilder();
364    for(int i = 0; i < prefix.length(); i++) {
365      b.append(' ');
366    }
367    b.append("->");
368    getReferredINode().dumpTreeRecursively(out, b, snapshot);
369  }
370  
371  public int getDstSnapshotId() {
372    return Snapshot.CURRENT_STATE_ID;
373  }
374  
375  /** An anonymous reference with reference count. */
376  public static class WithCount extends INodeReference {
377    
378    private final List<WithName> withNameList = new ArrayList<WithName>();
379    
380    /**
381     * Compare snapshot with IDs, where null indicates the current status thus
382     * is greater than any non-null snapshot.
383     */
384    public static final Comparator<WithName> WITHNAME_COMPARATOR
385        = new Comparator<WithName>() {
386      @Override
387      public int compare(WithName left, WithName right) {
388        return left.lastSnapshotId - right.lastSnapshotId;
389      }
390    };
391    
392    public WithCount(INodeReference parent, INode referred) {
393      super(parent, referred);
394      Preconditions.checkArgument(!referred.isReference());
395      referred.setParentReference(this);
396    }
397    
398    public int getReferenceCount() {
399      int count = withNameList.size();
400      if (getParentReference() != null) {
401        count++;
402      }
403      return count;
404    }
405
406    /** Increment and then return the reference count. */
407    public void addReference(INodeReference ref) {
408      if (ref instanceof WithName) {
409        WithName refWithName = (WithName) ref;
410        int i = Collections.binarySearch(withNameList, refWithName,
411            WITHNAME_COMPARATOR);
412        Preconditions.checkState(i < 0);
413        withNameList.add(-i - 1, refWithName);
414      } else if (ref instanceof DstReference) {
415        setParentReference(ref);
416      }
417    }
418
419    /** Decrement and then return the reference count. */
420    public void removeReference(INodeReference ref) {
421      if (ref instanceof WithName) {
422        int i = Collections.binarySearch(withNameList, (WithName) ref,
423            WITHNAME_COMPARATOR);
424        if (i >= 0) {
425          withNameList.remove(i);
426        }
427      } else if (ref == getParentReference()) {
428        setParent(null);
429      }
430    }
431
432    /** Return the last WithName reference if there is any, null otherwise. */
433    public WithName getLastWithName() {
434      return withNameList.size() > 0 ? 
435          withNameList.get(withNameList.size() - 1) : null;
436    }
437    
438    WithName getPriorWithName(WithName post) {
439      int i = Collections.binarySearch(withNameList, post, WITHNAME_COMPARATOR);
440      if (i > 0) {
441        return withNameList.get(i - 1);
442      } else if (i == 0 || i == -1) {
443        return null;
444      } else {
445        return withNameList.get(-i - 2);
446      }
447    }
448
449    /**
450     * @return the WithName/DstReference node contained in the given snapshot.
451     */
452    public INodeReference getParentRef(int snapshotId) {
453      int start = 0;
454      int end = withNameList.size() - 1;
455      while (start < end) {
456        int mid = start + (end - start) / 2;
457        int sid = withNameList.get(mid).lastSnapshotId; 
458        if (sid == snapshotId) {
459          return withNameList.get(mid);
460        } else if (sid < snapshotId) {
461          start = mid + 1;
462        } else {
463          end = mid;
464        }
465      }
466      if (start < withNameList.size() &&
467          withNameList.get(start).lastSnapshotId >= snapshotId) {
468        return withNameList.get(start);
469      } else {
470        return this.getParentReference();
471      }
472    }
473  }
474  
475  /** A reference with a fixed name. */
476  public static class WithName extends INodeReference {
477
478    private final byte[] name;
479
480    /**
481     * The id of the last snapshot in the src tree when this WithName node was 
482     * generated. When calculating the quota usage of the referred node, only 
483     * the files/dirs existing when this snapshot was taken will be counted for 
484     * this WithName node and propagated along its ancestor path.
485     */
486    private final int lastSnapshotId;
487    
488    public WithName(INodeDirectory parent, WithCount referred, byte[] name,
489        int lastSnapshotId) {
490      super(parent, referred);
491      this.name = name;
492      this.lastSnapshotId = lastSnapshotId;
493      referred.addReference(this);
494    }
495
496    @Override
497    public final byte[] getLocalNameBytes() {
498      return name;
499    }
500
501    @Override
502    public final void setLocalName(byte[] name) {
503      throw new UnsupportedOperationException("Cannot set name: " + getClass()
504          + " is immutable.");
505    }
506    
507    public int getLastSnapshotId() {
508      return lastSnapshotId;
509    }
510    
511    @Override
512    public final ContentSummaryComputationContext computeContentSummary(
513        ContentSummaryComputationContext summary) {
514      //only count storagespace for WithName
515      final QuotaCounts q = new QuotaCounts.Builder().build();
516      computeQuotaUsage(summary.getBlockStoragePolicySuite(),
517          getStoragePolicyID(), q, false, lastSnapshotId);
518      summary.getCounts().addContent(Content.DISKSPACE, q.getStorageSpace());
519      summary.getCounts().addTypeSpaces(q.getTypeSpaces());
520      return summary;
521    }
522
523    @Override
524    public final QuotaCounts computeQuotaUsage(BlockStoragePolicySuite bsps,
525        byte blockStoragePolicyId, QuotaCounts counts, boolean useCache,
526        int lastSnapshotId) {
527      // if this.lastSnapshotId < lastSnapshotId, the rename of the referred 
528      // node happened before the rename of its ancestor. This should be 
529      // impossible since for WithName node we only count its children at the 
530      // time of the rename. 
531      Preconditions.checkState(lastSnapshotId == Snapshot.CURRENT_STATE_ID
532          || this.lastSnapshotId >= lastSnapshotId);
533      final INode referred = this.getReferredINode().asReference()
534          .getReferredINode();
535      // We will continue the quota usage computation using the same snapshot id
536      // as time line (if the given snapshot id is valid). Also, we cannot use 
537      // cache for the referred node since its cached quota may have already 
538      // been updated by changes in the current tree.
539      int id = lastSnapshotId != Snapshot.CURRENT_STATE_ID ? 
540          lastSnapshotId : this.lastSnapshotId;
541      return referred.computeQuotaUsage(bsps, blockStoragePolicyId, counts,
542          false, id);
543    }
544    
545    @Override
546    public QuotaCounts cleanSubtree(BlockStoragePolicySuite bsps,
547        final int snapshot, int prior, final BlocksMapUpdateInfo collectedBlocks,
548        final List<INode> removedINodes) {
549      // since WithName node resides in deleted list acting as a snapshot copy,
550      // the parameter snapshot must be non-null
551      Preconditions.checkArgument(snapshot != Snapshot.CURRENT_STATE_ID);
552      // if prior is NO_SNAPSHOT_ID, we need to check snapshot belonging to the
553      // previous WithName instance
554      if (prior == Snapshot.NO_SNAPSHOT_ID) {
555        prior = getPriorSnapshot(this);
556      }
557      
558      if (prior != Snapshot.NO_SNAPSHOT_ID
559          && Snapshot.ID_INTEGER_COMPARATOR.compare(snapshot, prior) <= 0) {
560        return new QuotaCounts.Builder().build();
561      }
562
563      QuotaCounts counts = getReferredINode().cleanSubtree(bsps, snapshot, prior,
564          collectedBlocks, removedINodes);
565      INodeReference ref = getReferredINode().getParentReference();
566      if (ref != null) {
567        try {
568          ref.addSpaceConsumed(counts.negation(), true);
569        } catch (QuotaExceededException e) {
570          Log.warn("Should not have QuotaExceededException");
571        }
572      }
573      
574      if (snapshot < lastSnapshotId) {
575        // for a WithName node, when we compute its quota usage, we only count
576        // in all the nodes existing at the time of the corresponding rename op.
577        // Thus if we are deleting a snapshot before/at the snapshot associated 
578        // with lastSnapshotId, we do not need to update the quota upwards.
579        counts = new QuotaCounts.Builder().build();
580      }
581      return counts;
582    }
583    
584    @Override
585    public void destroyAndCollectBlocks(BlockStoragePolicySuite bsps,
586        BlocksMapUpdateInfo collectedBlocks,
587        final List<INode> removedINodes) {
588      int snapshot = getSelfSnapshot();
589      if (removeReference(this) <= 0) {
590        getReferredINode().destroyAndCollectBlocks(bsps, collectedBlocks,
591            removedINodes);
592      } else {
593        int prior = getPriorSnapshot(this);
594        INode referred = getReferredINode().asReference().getReferredINode();
595        
596        if (snapshot != Snapshot.NO_SNAPSHOT_ID) {
597          if (prior != Snapshot.NO_SNAPSHOT_ID && snapshot <= prior) {
598            // the snapshot to be deleted has been deleted while traversing 
599            // the src tree of the previous rename operation. This usually 
600            // happens when rename's src and dst are under the same 
601            // snapshottable directory. E.g., the following operation sequence:
602            // 1. create snapshot s1 on /test
603            // 2. rename /test/foo/bar to /test/foo2/bar
604            // 3. create snapshot s2 on /test
605            // 4. rename foo2 again
606            // 5. delete snapshot s2
607            return;
608          }
609          try {
610            QuotaCounts counts = referred.cleanSubtree(bsps, snapshot, prior,
611                collectedBlocks, removedINodes);
612            INodeReference ref = getReferredINode().getParentReference();
613            if (ref != null) {
614              ref.addSpaceConsumed(counts.negation(), true);
615            }
616          } catch (QuotaExceededException e) {
617            LOG.error("should not exceed quota while snapshot deletion", e);
618          }
619        }
620      }
621    }
622    
623    private int getSelfSnapshot() {
624      INode referred = getReferredINode().asReference().getReferredINode();
625      int snapshot = Snapshot.NO_SNAPSHOT_ID;
626      if (referred.isFile() && referred.asFile().isWithSnapshot()) {
627        snapshot = referred.asFile().getDiffs().getPrior(lastSnapshotId);
628      } else if (referred.isDirectory()) {
629        DirectoryWithSnapshotFeature sf = referred.asDirectory()
630            .getDirectoryWithSnapshotFeature();
631        if (sf != null) {
632          snapshot = sf.getDiffs().getPrior(lastSnapshotId);
633        }
634      }
635      return snapshot;
636    }
637  }
638  
639  public static class DstReference extends INodeReference {
640    /**
641     * Record the latest snapshot of the dst subtree before the rename. For
642     * later operations on the moved/renamed files/directories, if the latest
643     * snapshot is after this dstSnapshot, changes will be recorded to the
644     * latest snapshot. Otherwise changes will be recorded to the snapshot
645     * belonging to the src of the rename.
646     * 
647     * {@link Snapshot#NO_SNAPSHOT_ID} means no dstSnapshot (e.g., src of the
648     * first-time rename).
649     */
650    private final int dstSnapshotId;
651    
652    @Override
653    public final int getDstSnapshotId() {
654      return dstSnapshotId;
655    }
656    
657    public DstReference(INodeDirectory parent, WithCount referred,
658        final int dstSnapshotId) {
659      super(parent, referred);
660      this.dstSnapshotId = dstSnapshotId;
661      referred.addReference(this);
662    }
663    
664    @Override
665    public QuotaCounts cleanSubtree(BlockStoragePolicySuite bsps, int snapshot, int prior,
666        BlocksMapUpdateInfo collectedBlocks, List<INode> removedINodes) {
667      if (snapshot == Snapshot.CURRENT_STATE_ID
668          && prior == Snapshot.NO_SNAPSHOT_ID) {
669        QuotaCounts counts = new QuotaCounts.Builder().build();
670        this.computeQuotaUsage(bsps, counts, true);
671        destroyAndCollectBlocks(bsps, collectedBlocks, removedINodes);
672        return counts;
673      } else {
674        // if prior is NO_SNAPSHOT_ID, we need to check snapshot belonging to 
675        // the previous WithName instance
676        if (prior == Snapshot.NO_SNAPSHOT_ID) {
677          prior = getPriorSnapshot(this);
678        }
679        // if prior is not NO_SNAPSHOT_ID, and prior is not before the
680        // to-be-deleted snapshot, we can quit here and leave the snapshot
681        // deletion work to the src tree of rename
682        if (snapshot != Snapshot.CURRENT_STATE_ID
683            && prior != Snapshot.NO_SNAPSHOT_ID
684            && Snapshot.ID_INTEGER_COMPARATOR.compare(snapshot, prior) <= 0) {
685          return new QuotaCounts.Builder().build();
686        }
687        return getReferredINode().cleanSubtree(bsps, snapshot, prior,
688            collectedBlocks, removedINodes);
689      }
690    }
691    
692    /**
693     * {@inheritDoc}
694     * <br/>
695     * To destroy a DstReference node, we first remove its link with the 
696     * referred node. If the reference number of the referred node is <= 0, we 
697     * destroy the subtree of the referred node. Otherwise, we clean the 
698     * referred node's subtree and delete everything created after the last 
699     * rename operation, i.e., everything outside of the scope of the prior 
700     * WithName nodes.
701     */
702    @Override
703    public void destroyAndCollectBlocks(BlockStoragePolicySuite bsps,
704        BlocksMapUpdateInfo collectedBlocks, final List<INode> removedINodes) {
705      if (removeReference(this) <= 0) {
706        getReferredINode().destroyAndCollectBlocks(bsps, collectedBlocks,
707            removedINodes);
708      } else {
709        // we will clean everything, including files, directories, and 
710        // snapshots, that were created after this prior snapshot
711        int prior = getPriorSnapshot(this);
712        // prior must be non-null, otherwise we do not have any previous 
713        // WithName nodes, and the reference number will be 0.
714        Preconditions.checkState(prior != Snapshot.NO_SNAPSHOT_ID);
715        // identify the snapshot created after prior
716        int snapshot = getSelfSnapshot(prior);
717        
718        INode referred = getReferredINode().asReference().getReferredINode();
719        if (referred.isFile()) {
720          // if referred is a file, it must be a file with snapshot since we did
721          // recordModification before the rename
722          INodeFile file = referred.asFile();
723          Preconditions.checkState(file.isWithSnapshot());
724          // make sure we mark the file as deleted
725          file.getFileWithSnapshotFeature().deleteCurrentFile();
726          // when calling cleanSubtree of the referred node, since we
727          // compute quota usage updates before calling this destroy
728          // function, we use true for countDiffChange
729          referred.cleanSubtree(bsps, snapshot, prior, collectedBlocks,
730              removedINodes);
731        } else if (referred.isDirectory()) {
732          // similarly, if referred is a directory, it must be an
733          // INodeDirectory with snapshot
734          INodeDirectory dir = referred.asDirectory();
735          Preconditions.checkState(dir.isWithSnapshot());
736          try {
737            DirectoryWithSnapshotFeature.destroyDstSubtree(bsps, dir, snapshot,
738                prior, collectedBlocks, removedINodes);
739          } catch (QuotaExceededException e) {
740            LOG.error("should not exceed quota while snapshot deletion", e);
741          }
742        }
743      }
744    }
745
746    private int getSelfSnapshot(final int prior) {
747      WithCount wc = (WithCount) getReferredINode().asReference();
748      INode referred = wc.getReferredINode();
749      int lastSnapshot = Snapshot.CURRENT_STATE_ID;
750      if (referred.isFile() && referred.asFile().isWithSnapshot()) {
751        lastSnapshot = referred.asFile().getDiffs().getLastSnapshotId();
752      } else if (referred.isDirectory()) {
753        DirectoryWithSnapshotFeature sf = referred.asDirectory()
754            .getDirectoryWithSnapshotFeature();
755        if (sf != null) {
756          lastSnapshot = sf.getLastSnapshotId();
757        }
758      }
759      if (lastSnapshot != Snapshot.CURRENT_STATE_ID && lastSnapshot != prior) {
760        return lastSnapshot;
761      } else {
762        return Snapshot.CURRENT_STATE_ID;
763      }
764    }
765  }
766}