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 org.apache.hadoop.HadoopIllegalArgumentException; 021import org.apache.hadoop.fs.PathIsNotDirectoryException; 022import org.apache.hadoop.fs.StorageType; 023import org.apache.hadoop.fs.UnresolvedLinkException; 024import org.apache.hadoop.fs.XAttr; 025import org.apache.hadoop.fs.XAttrSetFlag; 026import org.apache.hadoop.fs.permission.FsAction; 027import org.apache.hadoop.fs.permission.FsPermission; 028import org.apache.hadoop.hdfs.protocol.Block; 029import org.apache.hadoop.hdfs.protocol.BlockStoragePolicy; 030import org.apache.hadoop.hdfs.protocol.HdfsConstants; 031import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; 032import org.apache.hadoop.hdfs.protocol.QuotaExceededException; 033import org.apache.hadoop.hdfs.protocol.SnapshotAccessControlException; 034import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager; 035import org.apache.hadoop.hdfs.server.blockmanagement.BlockStoragePolicySuite; 036import org.apache.hadoop.hdfs.util.EnumCounters; 037import org.apache.hadoop.security.AccessControlException; 038 039import java.io.FileNotFoundException; 040import java.io.IOException; 041import java.util.Arrays; 042import java.util.EnumSet; 043import java.util.List; 044 045import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACCESSTIME_PRECISION_KEY; 046import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_QUOTA_BY_STORAGETYPE_ENABLED_KEY; 047import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_STORAGE_POLICY_ENABLED_KEY; 048 049public class FSDirAttrOp { 050 static HdfsFileStatus setPermission( 051 FSDirectory fsd, final String srcArg, FsPermission permission) 052 throws IOException { 053 String src = srcArg; 054 FSPermissionChecker pc = fsd.getPermissionChecker(); 055 INodesInPath iip; 056 fsd.writeLock(); 057 try { 058 iip = fsd.resolvePathForWrite(pc, src); 059 src = iip.getPath(); 060 fsd.checkOwner(pc, iip); 061 unprotectedSetPermission(fsd, src, permission); 062 } finally { 063 fsd.writeUnlock(); 064 } 065 fsd.getEditLog().logSetPermissions(src, permission); 066 return fsd.getAuditFileInfo(iip); 067 } 068 069 static HdfsFileStatus setOwner( 070 FSDirectory fsd, String src, String username, String group) 071 throws IOException { 072 FSPermissionChecker pc = fsd.getPermissionChecker(); 073 INodesInPath iip; 074 fsd.writeLock(); 075 try { 076 iip = fsd.resolvePathForWrite(pc, src); 077 src = iip.getPath(); 078 fsd.checkOwner(pc, iip); 079 if (!pc.isSuperUser()) { 080 if (username != null && !pc.getUser().equals(username)) { 081 throw new AccessControlException("User " + username 082 + " is not a super user (non-super user cannot change owner)."); 083 } 084 if (group != null && !pc.containsGroup(group)) { 085 throw new AccessControlException( 086 "User " + username + " does not belong to " + group); 087 } 088 } 089 unprotectedSetOwner(fsd, src, username, group); 090 } finally { 091 fsd.writeUnlock(); 092 } 093 fsd.getEditLog().logSetOwner(src, username, group); 094 return fsd.getAuditFileInfo(iip); 095 } 096 097 static HdfsFileStatus setTimes( 098 FSDirectory fsd, String src, long mtime, long atime) 099 throws IOException { 100 if (!fsd.isAccessTimeSupported() && atime != -1) { 101 throw new IOException( 102 "Access time for hdfs is not configured. " + 103 " Please set " + DFS_NAMENODE_ACCESSTIME_PRECISION_KEY 104 + " configuration parameter."); 105 } 106 107 FSPermissionChecker pc = fsd.getPermissionChecker(); 108 109 INodesInPath iip; 110 fsd.writeLock(); 111 try { 112 iip = fsd.resolvePathForWrite(pc, src); 113 src = iip.getPath(); 114 // Write access is required to set access and modification times 115 if (fsd.isPermissionEnabled()) { 116 fsd.checkPathAccess(pc, iip, FsAction.WRITE); 117 } 118 final INode inode = iip.getLastINode(); 119 if (inode == null) { 120 throw new FileNotFoundException("File/Directory " + src + 121 " does not exist."); 122 } 123 boolean changed = unprotectedSetTimes(fsd, inode, mtime, atime, true, 124 iip.getLatestSnapshotId()); 125 if (changed) { 126 fsd.getEditLog().logTimes(src, mtime, atime); 127 } 128 } finally { 129 fsd.writeUnlock(); 130 } 131 return fsd.getAuditFileInfo(iip); 132 } 133 134 static boolean setReplication( 135 FSDirectory fsd, BlockManager bm, String src, final short replication) 136 throws IOException { 137 bm.verifyReplication(src, replication, null); 138 final boolean isFile; 139 FSPermissionChecker pc = fsd.getPermissionChecker(); 140 fsd.writeLock(); 141 try { 142 final INodesInPath iip = fsd.resolvePathForWrite(pc, src); 143 src = iip.getPath(); 144 if (fsd.isPermissionEnabled()) { 145 fsd.checkPathAccess(pc, iip, FsAction.WRITE); 146 } 147 148 final short[] blockRepls = new short[2]; // 0: old, 1: new 149 final Block[] blocks = unprotectedSetReplication(fsd, src, replication, 150 blockRepls); 151 isFile = blocks != null; 152 if (isFile) { 153 fsd.getEditLog().logSetReplication(src, replication); 154 bm.setReplication(blockRepls[0], blockRepls[1], src, blocks); 155 } 156 } finally { 157 fsd.writeUnlock(); 158 } 159 return isFile; 160 } 161 162 static HdfsFileStatus setStoragePolicy( 163 FSDirectory fsd, BlockManager bm, String src, final String policyName) 164 throws IOException { 165 if (!fsd.isStoragePolicyEnabled()) { 166 throw new IOException( 167 "Failed to set storage policy since " 168 + DFS_STORAGE_POLICY_ENABLED_KEY + " is set to false."); 169 } 170 FSPermissionChecker pc = fsd.getPermissionChecker(); 171 INodesInPath iip; 172 fsd.writeLock(); 173 try { 174 src = FSDirectory.resolvePath(src, fsd); 175 iip = fsd.getINodesInPath4Write(src); 176 177 if (fsd.isPermissionEnabled()) { 178 fsd.checkPathAccess(pc, iip, FsAction.WRITE); 179 } 180 181 // get the corresponding policy and make sure the policy name is valid 182 BlockStoragePolicy policy = bm.getStoragePolicy(policyName); 183 if (policy == null) { 184 throw new HadoopIllegalArgumentException( 185 "Cannot find a block policy with the name " + policyName); 186 } 187 unprotectedSetStoragePolicy(fsd, bm, iip, policy.getId()); 188 fsd.getEditLog().logSetStoragePolicy(src, policy.getId()); 189 } finally { 190 fsd.writeUnlock(); 191 } 192 return fsd.getAuditFileInfo(iip); 193 } 194 195 static BlockStoragePolicy[] getStoragePolicies(BlockManager bm) 196 throws IOException { 197 return bm.getStoragePolicies(); 198 } 199 200 static long getPreferredBlockSize(FSDirectory fsd, String src) 201 throws IOException { 202 FSPermissionChecker pc = fsd.getPermissionChecker(); 203 fsd.readLock(); 204 try { 205 final INodesInPath iip = fsd.resolvePath(pc, src, false); 206 src = iip.getPath(); 207 if (fsd.isPermissionEnabled()) { 208 fsd.checkTraverse(pc, iip); 209 } 210 return INodeFile.valueOf(iip.getLastINode(), src) 211 .getPreferredBlockSize(); 212 } finally { 213 fsd.readUnlock(); 214 } 215 } 216 217 /** 218 * Set the namespace, storagespace and typespace quota for a directory. 219 * 220 * Note: This does not support ".inodes" relative path. 221 */ 222 static void setQuota(FSDirectory fsd, String src, long nsQuota, long ssQuota, 223 StorageType type) throws IOException { 224 if (fsd.isPermissionEnabled()) { 225 FSPermissionChecker pc = fsd.getPermissionChecker(); 226 pc.checkSuperuserPrivilege(); 227 } 228 229 fsd.writeLock(); 230 try { 231 INodeDirectory changed = unprotectedSetQuota(fsd, src, nsQuota, ssQuota, type); 232 if (changed != null) { 233 final QuotaCounts q = changed.getQuotaCounts(); 234 if (type == null) { 235 fsd.getEditLog().logSetQuota(src, q.getNameSpace(), q.getStorageSpace()); 236 } else { 237 fsd.getEditLog().logSetQuotaByStorageType( 238 src, q.getTypeSpaces().get(type), type); 239 } 240 } 241 } finally { 242 fsd.writeUnlock(); 243 } 244 } 245 246 static void unprotectedSetPermission( 247 FSDirectory fsd, String src, FsPermission permissions) 248 throws FileNotFoundException, UnresolvedLinkException, 249 QuotaExceededException, SnapshotAccessControlException { 250 assert fsd.hasWriteLock(); 251 final INodesInPath inodesInPath = fsd.getINodesInPath4Write(src, true); 252 final INode inode = inodesInPath.getLastINode(); 253 if (inode == null) { 254 throw new FileNotFoundException("File does not exist: " + src); 255 } 256 int snapshotId = inodesInPath.getLatestSnapshotId(); 257 inode.setPermission(permissions, snapshotId); 258 } 259 260 static void unprotectedSetOwner( 261 FSDirectory fsd, String src, String username, String groupname) 262 throws FileNotFoundException, UnresolvedLinkException, 263 QuotaExceededException, SnapshotAccessControlException { 264 assert fsd.hasWriteLock(); 265 final INodesInPath inodesInPath = fsd.getINodesInPath4Write(src, true); 266 INode inode = inodesInPath.getLastINode(); 267 if (inode == null) { 268 throw new FileNotFoundException("File does not exist: " + src); 269 } 270 if (username != null) { 271 inode = inode.setUser(username, inodesInPath.getLatestSnapshotId()); 272 } 273 if (groupname != null) { 274 inode.setGroup(groupname, inodesInPath.getLatestSnapshotId()); 275 } 276 } 277 278 static boolean setTimes( 279 FSDirectory fsd, INode inode, long mtime, long atime, boolean force, 280 int latestSnapshotId) throws QuotaExceededException { 281 fsd.writeLock(); 282 try { 283 return unprotectedSetTimes(fsd, inode, mtime, atime, force, 284 latestSnapshotId); 285 } finally { 286 fsd.writeUnlock(); 287 } 288 } 289 290 static boolean unprotectedSetTimes( 291 FSDirectory fsd, String src, long mtime, long atime, boolean force) 292 throws UnresolvedLinkException, QuotaExceededException { 293 assert fsd.hasWriteLock(); 294 final INodesInPath i = fsd.getINodesInPath(src, true); 295 return unprotectedSetTimes(fsd, i.getLastINode(), mtime, atime, 296 force, i.getLatestSnapshotId()); 297 } 298 299 /** 300 * See {@link org.apache.hadoop.hdfs.protocol.ClientProtocol#setQuota(String, 301 * long, long, StorageType)} 302 * for the contract. 303 * Sets quota for for a directory. 304 * @return INodeDirectory if any of the quotas have changed. null otherwise. 305 * @throws FileNotFoundException if the path does not exist. 306 * @throws PathIsNotDirectoryException if the path is not a directory. 307 * @throws QuotaExceededException if the directory tree size is 308 * greater than the given quota 309 * @throws UnresolvedLinkException if a symlink is encountered in src. 310 * @throws SnapshotAccessControlException if path is in RO snapshot 311 */ 312 static INodeDirectory unprotectedSetQuota( 313 FSDirectory fsd, String src, long nsQuota, long ssQuota, StorageType type) 314 throws FileNotFoundException, PathIsNotDirectoryException, 315 QuotaExceededException, UnresolvedLinkException, 316 SnapshotAccessControlException, UnsupportedActionException { 317 assert fsd.hasWriteLock(); 318 // sanity check 319 if ((nsQuota < 0 && nsQuota != HdfsConstants.QUOTA_DONT_SET && 320 nsQuota != HdfsConstants.QUOTA_RESET) || 321 (ssQuota < 0 && ssQuota != HdfsConstants.QUOTA_DONT_SET && 322 ssQuota != HdfsConstants.QUOTA_RESET)) { 323 throw new IllegalArgumentException("Illegal value for nsQuota or " + 324 "ssQuota : " + nsQuota + " and " + 325 ssQuota); 326 } 327 // sanity check for quota by storage type 328 if ((type != null) && (!fsd.isQuotaByStorageTypeEnabled() || 329 nsQuota != HdfsConstants.QUOTA_DONT_SET)) { 330 throw new UnsupportedActionException( 331 "Failed to set quota by storage type because either" + 332 DFS_QUOTA_BY_STORAGETYPE_ENABLED_KEY + " is set to " + 333 fsd.isQuotaByStorageTypeEnabled() + " or nsQuota value is illegal " + 334 nsQuota); 335 } 336 337 String srcs = FSDirectory.normalizePath(src); 338 final INodesInPath iip = fsd.getINodesInPath4Write(srcs, true); 339 INodeDirectory dirNode = INodeDirectory.valueOf(iip.getLastINode(), srcs); 340 if (dirNode.isRoot() && nsQuota == HdfsConstants.QUOTA_RESET) { 341 throw new IllegalArgumentException("Cannot clear namespace quota on root."); 342 } else { // a directory inode 343 final QuotaCounts oldQuota = dirNode.getQuotaCounts(); 344 final long oldNsQuota = oldQuota.getNameSpace(); 345 final long oldSsQuota = oldQuota.getStorageSpace(); 346 347 if (nsQuota == HdfsConstants.QUOTA_DONT_SET) { 348 nsQuota = oldNsQuota; 349 } 350 if (ssQuota == HdfsConstants.QUOTA_DONT_SET) { 351 ssQuota = oldSsQuota; 352 } 353 354 // unchanged space/namespace quota 355 if (type == null && oldNsQuota == nsQuota && oldSsQuota == ssQuota) { 356 return null; 357 } 358 359 // unchanged type quota 360 if (type != null) { 361 EnumCounters<StorageType> oldTypeQuotas = oldQuota.getTypeSpaces(); 362 if (oldTypeQuotas != null && oldTypeQuotas.get(type) == ssQuota) { 363 return null; 364 } 365 } 366 367 final int latest = iip.getLatestSnapshotId(); 368 dirNode.recordModification(latest); 369 dirNode.setQuota(fsd.getBlockStoragePolicySuite(), nsQuota, ssQuota, type); 370 return dirNode; 371 } 372 } 373 374 static Block[] unprotectedSetReplication( 375 FSDirectory fsd, String src, short replication, short[] blockRepls) 376 throws QuotaExceededException, UnresolvedLinkException, 377 SnapshotAccessControlException { 378 assert fsd.hasWriteLock(); 379 380 final INodesInPath iip = fsd.getINodesInPath4Write(src, true); 381 final INode inode = iip.getLastINode(); 382 if (inode == null || !inode.isFile()) { 383 return null; 384 } 385 INodeFile file = inode.asFile(); 386 final short oldBR = file.getBlockReplication(); 387 long size = file.computeFileSize(true, true); 388 389 // before setFileReplication, check for increasing block replication. 390 // if replication > oldBR, then newBR == replication. 391 // if replication < oldBR, we don't know newBR yet. 392 if (replication > oldBR) { 393 fsd.updateCount(iip, 0L, size, oldBR, replication, true); 394 } 395 396 file.setFileReplication(replication, iip.getLatestSnapshotId()); 397 398 final short newBR = file.getBlockReplication(); 399 // check newBR < oldBR case. 400 if (newBR < oldBR) { 401 fsd.updateCount(iip, 0L, size, oldBR, newBR, true); 402 } 403 404 if (blockRepls != null) { 405 blockRepls[0] = oldBR; 406 blockRepls[1] = newBR; 407 } 408 return file.getBlocks(); 409 } 410 411 static void unprotectedSetStoragePolicy( 412 FSDirectory fsd, BlockManager bm, INodesInPath iip, byte policyId) 413 throws IOException { 414 assert fsd.hasWriteLock(); 415 final INode inode = iip.getLastINode(); 416 if (inode == null) { 417 throw new FileNotFoundException("File/Directory does not exist: " 418 + iip.getPath()); 419 } 420 final int snapshotId = iip.getLatestSnapshotId(); 421 if (inode.isFile()) { 422 BlockStoragePolicy newPolicy = bm.getStoragePolicy(policyId); 423 if (newPolicy.isCopyOnCreateFile()) { 424 throw new HadoopIllegalArgumentException( 425 "Policy " + newPolicy + " cannot be set after file creation."); 426 } 427 428 BlockStoragePolicy currentPolicy = 429 bm.getStoragePolicy(inode.getLocalStoragePolicyID()); 430 431 if (currentPolicy != null && currentPolicy.isCopyOnCreateFile()) { 432 throw new HadoopIllegalArgumentException( 433 "Existing policy " + currentPolicy.getName() + 434 " cannot be changed after file creation."); 435 } 436 inode.asFile().setStoragePolicyID(policyId, snapshotId); 437 } else if (inode.isDirectory()) { 438 setDirStoragePolicy(fsd, inode.asDirectory(), policyId, snapshotId); 439 } else { 440 throw new FileNotFoundException(iip.getPath() 441 + " is not a file or directory"); 442 } 443 } 444 445 private static void setDirStoragePolicy( 446 FSDirectory fsd, INodeDirectory inode, byte policyId, 447 int latestSnapshotId) throws IOException { 448 List<XAttr> existingXAttrs = XAttrStorage.readINodeXAttrs(inode); 449 XAttr xAttr = BlockStoragePolicySuite.buildXAttr(policyId); 450 List<XAttr> newXAttrs = FSDirXAttrOp.setINodeXAttrs(fsd, existingXAttrs, 451 Arrays.asList(xAttr), 452 EnumSet.of( 453 XAttrSetFlag.CREATE, 454 XAttrSetFlag.REPLACE)); 455 XAttrStorage.updateINodeXAttrs(inode, newXAttrs, latestSnapshotId); 456 } 457 458 private static boolean unprotectedSetTimes( 459 FSDirectory fsd, INode inode, long mtime, long atime, boolean force, 460 int latest) throws QuotaExceededException { 461 assert fsd.hasWriteLock(); 462 boolean status = false; 463 if (mtime != -1) { 464 inode = inode.setModificationTime(mtime, latest); 465 status = true; 466 } 467 // if the last access time update was within the last precision interval, 468 // then no need to store access time 469 if (atime != -1 && (status || force || atime > inode.getAccessTime() + 470 fsd.getFSNamesystem().getAccessTimePrecision())) { 471 inode.setAccessTime(atime, latest); 472 status = true; 473 } 474 return status; 475 } 476}