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.hbase.security.access; 019 020import java.io.IOException; 021import java.util.Map; 022import org.apache.hadoop.conf.Configuration; 023import org.apache.hadoop.fs.FileStatus; 024import org.apache.hadoop.fs.Path; 025import org.apache.hadoop.hbase.HBaseInterfaceAudience; 026import org.apache.hadoop.hbase.HConstants; 027import org.apache.hadoop.hbase.TableName; 028import org.apache.hadoop.hbase.master.HMaster; 029import org.apache.hadoop.hbase.master.cleaner.BaseHFileCleanerDelegate; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.apache.yetus.audience.InterfaceStability; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035/** 036 * Implementation of a file cleaner that checks if a empty directory with no subdirs and subfiles is 037 * deletable when user scan snapshot feature is enabled 038 */ 039@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) 040@InterfaceStability.Evolving 041public class SnapshotScannerHDFSAclCleaner extends BaseHFileCleanerDelegate { 042 private static final Logger LOG = LoggerFactory.getLogger(SnapshotScannerHDFSAclCleaner.class); 043 044 private HMaster master; 045 private boolean userScanSnapshotEnabled = false; 046 047 @Override 048 public void init(Map<String, Object> params) { 049 if (params != null && params.containsKey(HMaster.MASTER)) { 050 this.master = (HMaster) params.get(HMaster.MASTER); 051 } 052 } 053 054 @Override 055 public void setConf(Configuration conf) { 056 super.setConf(conf); 057 userScanSnapshotEnabled = SnapshotScannerHDFSAclHelper.isAclSyncToHdfsEnabled(conf); 058 } 059 060 @Override 061 protected boolean isFileDeletable(FileStatus fStat) { 062 // This plugin does not handle the file deletions, so return true by default 063 return true; 064 } 065 066 @Override 067 public boolean isEmptyDirDeletable(Path dir) { 068 if (userScanSnapshotEnabled) { 069 /* 070 * If user scan snapshot feature is enabled(see HBASE-21995), then when namespace or table 071 * exists, the archive namespace or table directories should not be deleted because the HDFS 072 * acls are set at these directories; the archive data directory should not be deleted because 073 * the HDFS acls of global permission is set at this directory. 074 */ 075 return isEmptyArchiveDirDeletable(dir); 076 } 077 return true; 078 } 079 080 private boolean isEmptyArchiveDirDeletable(Path dir) { 081 try { 082 if (isArchiveDataDir(dir)) { 083 return false; 084 } else if (isArchiveNamespaceDir(dir) && namespaceExists(dir.getName())) { 085 return false; 086 } else if ( 087 isArchiveTableDir(dir) 088 && tableExists(TableName.valueOf(dir.getParent().getName(), dir.getName())) 089 ) { 090 return false; 091 } 092 return true; 093 } catch (IOException e) { 094 LOG.warn("Check if empty dir {} is deletable error", dir, e); 095 return false; 096 } 097 } 098 099 @InterfaceAudience.Private 100 static boolean isArchiveDataDir(Path path) { 101 if (path != null && path.getName().equals(HConstants.BASE_NAMESPACE_DIR)) { 102 Path parent = path.getParent(); 103 return parent != null && parent.getName().equals(HConstants.HFILE_ARCHIVE_DIRECTORY); 104 } 105 return false; 106 } 107 108 @InterfaceAudience.Private 109 static boolean isArchiveNamespaceDir(Path path) { 110 return path != null && isArchiveDataDir(path.getParent()); 111 } 112 113 @InterfaceAudience.Private 114 static boolean isArchiveTableDir(Path path) { 115 return path != null && isArchiveNamespaceDir(path.getParent()); 116 } 117 118 private boolean namespaceExists(String namespace) throws IOException { 119 return master != null && master.listNamespaces().contains(namespace); 120 } 121 122 private boolean tableExists(TableName tableName) throws IOException { 123 return master != null && master.getTableDescriptors().exists(tableName); 124 } 125}