/** Cast INode to INodeDirectorySnapshottable. */ public static INodeDirectorySnapshottable valueOf(INode inode, String src) throws IOException { final INodeDirectory dir = INodeDirectory.valueOf(inode, src); if (!dir.isSnapshottable()) { throw new SnapshotException("Directory is not a snapshottable directory: " + src); } return (INodeDirectorySnapshottable) dir; }
/** * Remove the snapshot with the given name from {@link #snapshotsByNames}, and delete all the * corresponding DirectoryDiff. * * @param snapshotName The name of the snapshot to be removed * @param collectedBlocks Used to collect information to update blocksMap * @return The removed snapshot. Null if no snapshot with the given name exists. */ Snapshot removeSnapshot( String snapshotName, BlocksMapUpdateInfo collectedBlocks, final List<INode> removedINodes) throws SnapshotException { final int i = searchSnapshot(DFSUtil.string2Bytes(snapshotName)); if (i < 0) { throw new SnapshotException( "Cannot delete snapshot " + snapshotName + " from path " + this.getFullPathName() + ": the snapshot does not exist."); } else { final Snapshot snapshot = snapshotsByNames.get(i); Snapshot prior = Snapshot.findLatestSnapshot(this, snapshot); try { Quota.Counts counts = cleanSubtree(snapshot, prior, collectedBlocks, removedINodes, true); INodeDirectory parent = getParent(); if (parent != null) { // there will not be any WithName node corresponding to the deleted // snapshot, thus only update the quota usage in the current tree parent.addSpaceConsumed(-counts.get(Quota.NAMESPACE), -counts.get(Quota.DISKSPACE), true); } } catch (QuotaExceededException e) { LOG.error("BUG: removeSnapshot increases namespace usage.", e); } // remove from snapshotsByNames after successfully cleaning the subtree snapshotsByNames.remove(i); return snapshot; } }
/** * Recursively compute the difference between snapshots under a given directory/file. * * @param node The directory/file under which the diff is computed. * @param parentPath Relative path (corresponding to the snapshot root) of the node's parent. * @param diffReport data structure used to store the diff. */ private void computeDiffRecursively( INode node, List<byte[]> parentPath, SnapshotDiffInfo diffReport) { ChildrenDiff diff = new ChildrenDiff(); byte[][] relativePath = parentPath.toArray(new byte[parentPath.size()][]); if (node.isDirectory()) { INodeDirectory dir = node.asDirectory(); if (dir instanceof INodeDirectoryWithSnapshot) { INodeDirectoryWithSnapshot sdir = (INodeDirectoryWithSnapshot) dir; boolean change = sdir.computeDiffBetweenSnapshots(diffReport.from, diffReport.to, diff); if (change) { diffReport.addDirDiff(sdir, relativePath, diff); } } ReadOnlyList<INode> children = dir.getChildrenList(diffReport.isFromEarlier() ? diffReport.to : diffReport.from); for (INode child : children) { final byte[] name = child.getLocalNameBytes(); if (diff.searchIndex(ListType.CREATED, name) < 0 && diff.searchIndex(ListType.DELETED, name) < 0) { parentPath.add(name); computeDiffRecursively(child, parentPath, diffReport); parentPath.remove(parentPath.size() - 1); } } } else if (node.isFile() && node.asFile() instanceof FileWithSnapshot) { FileWithSnapshot file = (FileWithSnapshot) node.asFile(); Snapshot earlierSnapshot = diffReport.isFromEarlier() ? diffReport.from : diffReport.to; Snapshot laterSnapshot = diffReport.isFromEarlier() ? diffReport.to : diffReport.from; boolean change = file.getDiffs().changedBetweenSnapshots(earlierSnapshot, laterSnapshot); if (change) { diffReport.addFileDiff(file.asINodeFile(), relativePath); } } }
/** * Rename a snapshot * * @param path The directory path where the snapshot was taken. Used for generating exception * message. * @param oldName Old name of the snapshot * @param newName New name the snapshot will be renamed to * @throws SnapshotException Throw SnapshotException when either the snapshot with the old name * does not exist or a snapshot with the new name already exists */ public void renameSnapshot(String path, String oldName, String newName) throws SnapshotException { if (newName.equals(oldName)) { return; } final int indexOfOld = searchSnapshot(DFSUtil.string2Bytes(oldName)); if (indexOfOld < 0) { throw new SnapshotException( "The snapshot " + oldName + " does not exist for directory " + path); } else { final byte[] newNameBytes = DFSUtil.string2Bytes(newName); int indexOfNew = searchSnapshot(newNameBytes); if (indexOfNew > 0) { throw new SnapshotException( "The snapshot " + newName + " already exists for directory " + path); } // remove the one with old name from snapshotsByNames Snapshot snapshot = snapshotsByNames.remove(indexOfOld); final INodeDirectory ssRoot = snapshot.getRoot(); ssRoot.setLocalName(newNameBytes); indexOfNew = -indexOfNew - 1; if (indexOfNew <= indexOfOld) { snapshotsByNames.add(indexOfNew, snapshot); } else { // indexOfNew > indexOfOld snapshotsByNames.add(indexOfNew - 1, snapshot); } } }
/** * Find the latest snapshot that 1) covers the given inode (which means the snapshot was either * taken on the inode or taken on an ancestor of the inode), and 2) was taken before the given * snapshot (if the given snapshot is not null). * * @param inode the given inode that the returned snapshot needs to cover * @param anchor the returned snapshot should be taken before this given id. * @return id of the latest snapshot that covers the given inode and was taken before the the * given snapshot (if it is not null). */ public static int findLatestSnapshot(INode inode, final int anchor) { int latest = NO_SNAPSHOT_ID; for (; inode != null; inode = inode.getParent()) { if (inode.isDirectory()) { final INodeDirectory dir = inode.asDirectory(); if (dir.isWithSnapshot()) { latest = dir.getDiffs().updatePrior(anchor, latest); } } } return latest; }
private void loadSnapshots(InputStream in, int size) throws IOException { for (int i = 0; i < size; i++) { SnapshotSection.Snapshot pbs = SnapshotSection.Snapshot.parseDelimitedFrom(in); INodeDirectory root = loadINodeDirectory(pbs.getRoot(), parent.getLoaderContext()); int sid = pbs.getSnapshotId(); INodeDirectorySnapshottable parent = (INodeDirectorySnapshottable) fsDir.getInode(root.getId()).asDirectory(); Snapshot snapshot = new Snapshot(sid, root, parent); // add the snapshot to parent, since we follow the sequence of // snapshotsByNames when saving, we do not need to sort when loading parent.addSnapshot(snapshot); snapshotMap.put(sid, snapshot); } }
private void serializeDirDiffList( INodeDirectory dir, final List<INodeReference> refList, OutputStream out) throws IOException { DirectoryWithSnapshotFeature sf = dir.getDirectoryWithSnapshotFeature(); if (sf != null) { List<DirectoryDiff> diffList = sf.getDiffs().asList(); SnapshotDiffSection.DiffEntry entry = SnapshotDiffSection.DiffEntry.newBuilder() .setInodeId(dir.getId()) .setType(Type.DIRECTORYDIFF) .setNumOfDiff(diffList.size()) .build(); entry.writeDelimitedTo(out); for (int i = diffList.size() - 1; i >= 0; i--) { // reverse order! DirectoryDiff diff = diffList.get(i); SnapshotDiffSection.DirectoryDiff.Builder db = SnapshotDiffSection.DirectoryDiff.newBuilder() .setSnapshotId(diff.getSnapshotId()) .setChildrenSize(diff.getChildrenSize()) .setIsSnapshotRoot(diff.isSnapshotRoot()); INodeDirectoryAttributes copy = diff.snapshotINode; if (!diff.isSnapshotRoot() && copy != null) { db.setName(ByteString.copyFrom(copy.getLocalNameBytes())) .setSnapshotCopy(buildINodeDirectory(copy, parent.getSaverContext())); } // process created list and deleted list List<INode> created = diff.getChildrenDiff().getList(ListType.CREATED); db.setCreatedListSize(created.size()); List<INode> deleted = diff.getChildrenDiff().getList(ListType.DELETED); for (INode d : deleted) { if (d.isReference()) { refList.add(d.asReference()); db.addDeletedINodeRef(refList.size() - 1); } else { db.addDeletedINode(d.getId()); } } db.build().writeDelimitedTo(out); saveCreatedList(created, out); } } }
/** * Load the snapshots section from fsimage. Also convert snapshottable directories into {@link * INodeDirectorySnapshottable}. */ public void loadSnapshotSection(InputStream in) throws IOException { SnapshotManager sm = fsn.getSnapshotManager(); SnapshotSection section = SnapshotSection.parseDelimitedFrom(in); int snum = section.getNumSnapshots(); sm.setNumSnapshots(snum); sm.setSnapshotCounter(section.getSnapshotCounter()); for (long sdirId : section.getSnapshottableDirList()) { INodeDirectory dir = fsDir.getInode(sdirId).asDirectory(); final INodeDirectorySnapshottable sdir; if (!dir.isSnapshottable()) { sdir = new INodeDirectorySnapshottable(dir); fsDir.addToInodeMap(sdir); } else { // dir is root, and admin set root to snapshottable before sdir = (INodeDirectorySnapshottable) dir; sdir.setSnapshotQuota(INodeDirectorySnapshottable.SNAPSHOT_LIMIT); } sm.addSnapshottable(sdir); } loadSnapshots(in, snum); }
Root(INodeDirectory other) { // Always preserve ACL, XAttr. super( other, false, Lists.newArrayList( Iterables.filter( Arrays.asList(other.getFeatures()), new Predicate<Feature>() { @Override public boolean apply(Feature input) { if (AclFeature.class.isInstance(input) || XAttrFeature.class.isInstance(input)) { return true; } return false; } })) .toArray(new Feature[0])); }
/** Load DirectoryDiff list for a directory with snapshot feature */ private void loadDirectoryDiffList( InputStream in, INodeDirectory dir, int size, final List<INodeReference> refList) throws IOException { if (!dir.isWithSnapshot()) { dir.addSnapshotFeature(null); } DirectoryDiffList diffs = dir.getDiffs(); final LoaderContext state = parent.getLoaderContext(); for (int i = 0; i < size; i++) { // load a directory diff SnapshotDiffSection.DirectoryDiff diffInPb = SnapshotDiffSection.DirectoryDiff.parseDelimitedFrom(in); final int snapshotId = diffInPb.getSnapshotId(); final Snapshot snapshot = snapshotMap.get(snapshotId); int childrenSize = diffInPb.getChildrenSize(); boolean useRoot = diffInPb.getIsSnapshotRoot(); INodeDirectoryAttributes copy = null; if (useRoot) { copy = snapshot.getRoot(); } else if (diffInPb.hasSnapshotCopy()) { INodeSection.INodeDirectory dirCopyInPb = diffInPb.getSnapshotCopy(); final byte[] name = diffInPb.getName().toByteArray(); PermissionStatus permission = loadPermission(dirCopyInPb.getPermission(), state.getStringTable()); AclFeature acl = null; if (dirCopyInPb.hasAcl()) { acl = new AclFeature( FSImageFormatPBINode.Loader.loadAclEntries( dirCopyInPb.getAcl(), state.getStringTable())); } long modTime = dirCopyInPb.getModificationTime(); boolean noQuota = dirCopyInPb.getNsQuota() == -1 && dirCopyInPb.getDsQuota() == -1; copy = noQuota ? new INodeDirectoryAttributes.SnapshotCopy(name, permission, acl, modTime) : new INodeDirectoryAttributes.CopyWithQuota( name, permission, acl, modTime, dirCopyInPb.getNsQuota(), dirCopyInPb.getDsQuota()); } // load created list List<INode> clist = loadCreatedList(in, dir, diffInPb.getCreatedListSize()); // load deleted list List<INode> dlist = loadDeletedList( refList, in, dir, diffInPb.getDeletedINodeList(), diffInPb.getDeletedINodeRefList()); // create the directory diff DirectoryDiff diff = new DirectoryDiff(snapshotId, copy, null, childrenSize, clist, dlist, useRoot); diffs.addFirst(diff); } }