/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.mapreduce.server.tasktracker; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapred.TaskController; import org.apache.hadoop.mapred.TaskLog; import org.apache.hadoop.mapred.TaskTracker; import org.apache.hadoop.mapred.TaskController.InitializationContext; import org.apache.hadoop.mapreduce.JobID; /** * * NOTE: This class is internal only and not intended for users!! */ public class Localizer { static final Log LOG = LogFactory.getLog(Localizer.class); private FileSystem fs; private String[] localDirs; private TaskController taskController; /** * Create a Localizer instance * * @param fileSys * @param lDirs * @param tc */ public Localizer(FileSystem fileSys, String[] lDirs, TaskController tc) { fs = fileSys; localDirs = lDirs; taskController = tc; } /** * NOTE: This class is internal only class and not intended for users!! * */ public static class PermissionsHandler { /** * Permission information useful for setting permissions for a given path. * Using this, one can set all possible combinations of permissions for the * owner of the file. But permissions for the group and all others can only * be set together, i.e. permissions for group cannot be set different from * those for others and vice versa. */ public static class PermissionsInfo { public boolean readPermissions; public boolean writePermissions; public boolean executablePermissions; public boolean readPermsOwnerOnly; public boolean writePermsOwnerOnly; public boolean executePermsOwnerOnly; /** * Create a permissions-info object with the given attributes * * @param readPerms * @param writePerms * @param executePerms * @param readOwnerOnly * @param writeOwnerOnly * @param executeOwnerOnly */ public PermissionsInfo(boolean readPerms, boolean writePerms, boolean executePerms, boolean readOwnerOnly, boolean writeOwnerOnly, boolean executeOwnerOnly) { readPermissions = readPerms; writePermissions = writePerms; executablePermissions = executePerms; readPermsOwnerOnly = readOwnerOnly; writePermsOwnerOnly = writeOwnerOnly; executePermsOwnerOnly = executeOwnerOnly; } } /** * Set permission on the given file path using the specified permissions * information. We use java api to set permission instead of spawning chmod * processes. This saves a lot of time. Using this, one can set all possible * combinations of permissions for the owner of the file. But permissions * for the group and all others can only be set together, i.e. permissions * for group cannot be set different from those for others and vice versa. * * This method should satisfy the needs of most of the applications. For * those it doesn't, {@link FileUtil#chmod} can be used. * * @param f file path * @param pInfo permissions information * @return true if success, false otherwise */ public static boolean setPermissions(File f, PermissionsInfo pInfo) { if (pInfo == null) { LOG.debug(" PermissionsInfo is null, returning."); return true; } LOG.debug("Setting permission for " + f.getAbsolutePath()); boolean ret = true; // Clear all the flags ret = f.setReadable(false, false) && ret; ret = f.setWritable(false, false) && ret; ret = f.setExecutable(false, false) && ret; ret = f.setReadable(pInfo.readPermissions, pInfo.readPermsOwnerOnly); LOG.debug("Readable status for " + f + " set to " + ret); ret = f.setWritable(pInfo.writePermissions, pInfo.writePermsOwnerOnly) && ret; LOG.debug("Writable status for " + f + " set to " + ret); ret = f.setExecutable(pInfo.executablePermissions, pInfo.executePermsOwnerOnly) && ret; LOG.debug("Executable status for " + f + " set to " + ret); return ret; } /** * Permissions rwxr_xr_x */ public static final PermissionsInfo sevenFiveFive = new PermissionsInfo(true, true, true, false, true, false); /** * Completely private permissions */ public static final PermissionsInfo sevenZeroZero = new PermissionsInfo(true, true, true, true, true, true); } // Data-structure for synchronizing localization of user directories. private Map<String, AtomicBoolean> localizedUsers = new HashMap<String, AtomicBoolean>(); /** * Initialize the local directories for a particular user on this TT. This * involves creation and setting permissions of the following directories * <ul> * <li>$mapred.local.dir/taskTracker/$user</li> * <li>$mapred.local.dir/taskTracker/$user/jobcache</li> * <li>$mapred.local.dir/taskTracker/$user/distcache</li> * </ul> * * @param user * @throws IOException */ public void initializeUserDirs(String user) throws IOException { if (user == null) { // This shouldn't happen in general throw new IOException( "User is null. Cannot initialized user-directories."); } AtomicBoolean localizedUser; synchronized (localizedUsers) { if (!localizedUsers.containsKey(user)) { localizedUsers.put(user, new AtomicBoolean(false)); } localizedUser = localizedUsers.get(user); } synchronized (localizedUser) { if (localizedUser.get()) { // User-directories are already localized for this user. LOG.info("User-directories for the user " + user + " are already initialized on this TT. Not doing anything."); return; } LOG.info("Initializing user " + user + " on this TT."); boolean userDirStatus = false; boolean jobCacheDirStatus = false; boolean distributedCacheDirStatus = false; for (String localDir : localDirs) { Path userDir = new Path(localDir, TaskTracker.getUserDir(user)); // Set up the user-directory. if (fs.exists(userDir) || fs.mkdirs(userDir)) { // Set permissions on the user-directory PermissionsHandler.setPermissions( new File(userDir.toUri().getPath()), PermissionsHandler.sevenZeroZero); userDirStatus = true; // Set up the jobcache directory File jobCacheDir = new File(localDir, TaskTracker.getJobCacheSubdir(user)); if (jobCacheDir.exists() || jobCacheDir.mkdirs()) { // Set permissions on the jobcache-directory PermissionsHandler.setPermissions(jobCacheDir, PermissionsHandler.sevenZeroZero); jobCacheDirStatus = true; } else { LOG.warn("Unable to create job cache directory : " + jobCacheDir.getPath()); } // Set up the cache directory used for distributed cache files File distributedCacheDir = new File(localDir, TaskTracker.getPrivateDistributedCacheDir(user)); if (distributedCacheDir.exists() || distributedCacheDir.mkdirs()) { // Set permissions on the distcache-directory PermissionsHandler.setPermissions(distributedCacheDir, PermissionsHandler.sevenZeroZero); distributedCacheDirStatus = true; } else { LOG.warn("Unable to create distributed-cache directory : " + distributedCacheDir.getPath()); } } else { LOG.warn("Unable to create the user directory : " + userDir); } } if (!userDirStatus) { throw new IOException("Not able to initialize user directories " + "in any of the configured local directories for user " + user); } if (!jobCacheDirStatus) { throw new IOException("Not able to initialize job-cache directories " + "in any of the configured local directories for user " + user); } if (!distributedCacheDirStatus) { throw new IOException( "Not able to initialize distributed-cache directories " + "in any of the configured local directories for user " + user); } // Now, run the task-controller specific code to initialize the // user-directories. InitializationContext context = new InitializationContext(); context.user = user; context.workDir = null; taskController.initializeUser(context); // Localization of the user is done localizedUser.set(true); } } /** * Prepare the job directories for a given job. To be called by the job * localization code, only if the job is not already localized. * * <br> * Here, we set 700 permissions on the job directories created on all disks. * This we do so as to avoid any misuse by other users till the time * {@link TaskController#initializeJob(JobInitializationContext)} is run at a * later time to set proper private permissions on the job directories. <br> * * @param user * @param jobId * @throws IOException */ public void initializeJobDirs(String user, JobID jobId) throws IOException { boolean initJobDirStatus = false; String jobDirPath = TaskTracker.getLocalJobDir(user, jobId.toString()); for (String localDir : localDirs) { Path jobDir = new Path(localDir, jobDirPath); if (fs.exists(jobDir)) { // this will happen on a partial execution of localizeJob. Sometimes // copying job.xml to the local disk succeeds but copying job.jar might // throw out an exception. We should clean up and then try again. fs.delete(jobDir, true); } boolean jobDirStatus = fs.mkdirs(jobDir); if (!jobDirStatus) { LOG.warn("Not able to create job directory " + jobDir.toString()); } initJobDirStatus = initJobDirStatus || jobDirStatus; // job-dir has to be private to the TT Localizer.PermissionsHandler.setPermissions(new File(jobDir.toUri() .getPath()), Localizer.PermissionsHandler.sevenZeroZero); } if (!initJobDirStatus) { throw new IOException("Not able to initialize job directories " + "in any of the configured local directories for job " + jobId.toString()); } } /** * Create taskDirs on all the disks. Otherwise, in some cases, like when * LinuxTaskController is in use, child might wish to balance load across * disks but cannot itself create attempt directory because of the fact that * job directory is writable only by the TT. * * @param user * @param jobId * @param attemptId * @param isCleanupAttempt * @throws IOException */ public void initializeAttemptDirs(String user, String jobId, String attemptId, boolean isCleanupAttempt) throws IOException { boolean initStatus = false; String attemptDirPath = TaskTracker.getLocalTaskDir(user, jobId, attemptId, isCleanupAttempt); for (String localDir : localDirs) { Path localAttemptDir = new Path(localDir, attemptDirPath); boolean attemptDirStatus = fs.mkdirs(localAttemptDir); if (!attemptDirStatus) { LOG.warn("localAttemptDir " + localAttemptDir.toString() + " couldn't be created."); } initStatus = initStatus || attemptDirStatus; } if (!initStatus) { throw new IOException("Not able to initialize attempt directories " + "in any of the configured local directories for the attempt " + attemptId); } } /** * Create job log directory and set appropriate permissions for the directory. * * @param jobId */ public void initializeJobLogDir(JobID jobId) { File jobUserLogDir = TaskLog.getJobDir(jobId); if (!jobUserLogDir.exists()) { boolean ret = jobUserLogDir.mkdirs(); if (!ret) { LOG.warn("Could not create job user log directory: " + jobUserLogDir); return; } } Localizer.PermissionsHandler.setPermissions(jobUserLogDir, Localizer.PermissionsHandler.sevenZeroZero); } }