/** * 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.fs; import static org.apache.hadoop.fs.FileContextTestHelper.getAbsoluteTestRootDir; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.IOException; import java.net.URI; import org.apache.commons.logging.impl.Log4JLogger; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.protocol.HdfsConstants; import org.apache.hadoop.hdfs.server.namenode.NameNode; import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; import org.apache.hadoop.hdfs.web.WebHdfsTestUtil; import org.apache.hadoop.ipc.RemoteException; import org.apache.log4j.Level; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * Test symbolic links using FileContext and Hdfs. */ public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest { { ((Log4JLogger)NameNode.stateChangeLog).getLogger().setLevel(Level.ALL); } private static MiniDFSCluster cluster; private static WebHdfsFileSystem webhdfs; protected String getScheme() { return "hdfs"; } protected String testBaseDir1() throws IOException { return "/test1"; } protected String testBaseDir2() throws IOException { return "/test2"; } protected URI testURI() { return cluster.getURI(0); } @Override protected IOException unwrapException(IOException e) { if (e instanceof RemoteException) { return ((RemoteException)e).unwrapRemoteException(); } return e; } @BeforeClass public static void testSetUp() throws Exception { Configuration conf = new HdfsConfiguration(); conf.setBoolean(DFSConfigKeys.DFS_WEBHDFS_ENABLED_KEY, true); conf.set(FsPermission.UMASK_LABEL, "000"); cluster = new MiniDFSCluster.Builder(conf).build(); fc = FileContext.getFileContext(cluster.getURI(0)); webhdfs = WebHdfsTestUtil.getWebHdfsFileSystem(conf); } @AfterClass public static void testTearDown() throws Exception { cluster.shutdown(); } @Test /** Link from Hdfs to LocalFs */ public void testLinkAcrossFileSystems() throws IOException { Path localDir = new Path("file://"+getAbsoluteTestRootDir(fc)+"/test"); Path localFile = new Path("file://"+getAbsoluteTestRootDir(fc)+"/test/file"); Path link = new Path(testBaseDir1(), "linkToFile"); FileContext localFc = FileContext.getLocalFSFileContext(); localFc.delete(localDir, true); localFc.mkdir(localDir, FileContext.DEFAULT_PERM, true); localFc.setWorkingDirectory(localDir); assertEquals(localDir, localFc.getWorkingDirectory()); createAndWriteFile(localFc, localFile); fc.createSymlink(localFile, link, false); readFile(link); assertEquals(fileSize, fc.getFileStatus(link).getLen()); } @Test /** Test access a symlink using AbstractFileSystem */ public void testAccessLinkFromAbstractFileSystem() throws IOException { Path file = new Path(testBaseDir1(), "file"); Path link = new Path(testBaseDir1(), "linkToFile"); createAndWriteFile(file); fc.createSymlink(file, link, false); try { AbstractFileSystem afs = fc.getDefaultFileSystem(); afs.open(link); fail("Opened a link using AFS"); } catch (UnresolvedLinkException x) { // Expected } } @Test /** Test create symlink to / */ public void testCreateLinkToSlash() throws IOException { Path dir = new Path(testBaseDir1()); Path file = new Path(testBaseDir1(), "file"); Path link = new Path(testBaseDir1(), "linkToSlash"); Path fileViaLink = new Path(testBaseDir1()+"/linkToSlash"+ testBaseDir1()+"/file"); createAndWriteFile(file); fc.setWorkingDirectory(dir); fc.createSymlink(new Path("/"), link, false); readFile(fileViaLink); assertEquals(fileSize, fc.getFileStatus(fileViaLink).getLen()); // Ditto when using another file context since the file system // for the slash is resolved according to the link's parent. FileContext localFc = FileContext.getLocalFSFileContext(); Path linkQual = new Path(cluster.getURI(0).toString(), fileViaLink); assertEquals(fileSize, localFc.getFileStatus(linkQual).getLen()); } @Test /** setPermission affects the target not the link */ public void testSetPermissionAffectsTarget() throws IOException { Path file = new Path(testBaseDir1(), "file"); Path dir = new Path(testBaseDir2()); Path linkToFile = new Path(testBaseDir1(), "linkToFile"); Path linkToDir = new Path(testBaseDir1(), "linkToDir"); createAndWriteFile(file); fc.createSymlink(file, linkToFile, false); fc.createSymlink(dir, linkToDir, false); // Changing the permissions using the link does not modify // the permissions of the link.. FsPermission perms = fc.getFileLinkStatus(linkToFile).getPermission(); fc.setPermission(linkToFile, new FsPermission((short)0664)); fc.setOwner(linkToFile, "user", "group"); assertEquals(perms, fc.getFileLinkStatus(linkToFile).getPermission()); // but the file's permissions were adjusted appropriately FileStatus stat = fc.getFileStatus(file); assertEquals(0664, stat.getPermission().toShort()); assertEquals("user", stat.getOwner()); assertEquals("group", stat.getGroup()); // Getting the file's permissions via the link is the same // as getting the permissions directly. assertEquals(stat.getPermission(), fc.getFileStatus(linkToFile).getPermission()); // Ditto for a link to a directory perms = fc.getFileLinkStatus(linkToDir).getPermission(); fc.setPermission(linkToDir, new FsPermission((short)0664)); fc.setOwner(linkToDir, "user", "group"); assertEquals(perms, fc.getFileLinkStatus(linkToDir).getPermission()); stat = fc.getFileStatus(dir); assertEquals(0664, stat.getPermission().toShort()); assertEquals("user", stat.getOwner()); assertEquals("group", stat.getGroup()); assertEquals(stat.getPermission(), fc.getFileStatus(linkToDir).getPermission()); } @Test /** Create a symlink using a path with scheme but no authority */ public void testCreateWithPartQualPathFails() throws IOException { Path fileWoAuth = new Path("hdfs:///test/file"); Path linkWoAuth = new Path("hdfs:///test/link"); try { createAndWriteFile(fileWoAuth); fail("HDFS requires URIs with schemes have an authority"); } catch (RuntimeException e) { // Expected } try { fc.createSymlink(new Path("foo"), linkWoAuth, false); fail("HDFS requires URIs with schemes have an authority"); } catch (RuntimeException e) { // Expected } } @Test /** setReplication affects the target not the link */ public void testSetReplication() throws IOException { Path file = new Path(testBaseDir1(), "file"); Path link = new Path(testBaseDir1(), "linkToFile"); createAndWriteFile(file); fc.createSymlink(file, link, false); fc.setReplication(link, (short)2); assertEquals(0, fc.getFileLinkStatus(link).getReplication()); assertEquals(2, fc.getFileStatus(link).getReplication()); assertEquals(2, fc.getFileStatus(file).getReplication()); } @Test /** Test create symlink with a max len name */ public void testCreateLinkMaxPathLink() throws IOException { Path dir = new Path(testBaseDir1()); Path file = new Path(testBaseDir1(), "file"); final int maxPathLen = HdfsConstants.MAX_PATH_LENGTH; final int dirLen = dir.toString().length() + 1; int len = maxPathLen - dirLen; // Build a MAX_PATH_LENGTH path StringBuilder sb = new StringBuilder(""); for (int i = 0; i < (len / 10); i++) { sb.append("0123456789"); } for (int i = 0; i < (len % 10); i++) { sb.append("x"); } Path link = new Path(sb.toString()); assertEquals(maxPathLen, dirLen + link.toString().length()); // Check that it works createAndWriteFile(file); fc.setWorkingDirectory(dir); fc.createSymlink(file, link, false); readFile(link); // Now modify the path so it's too large link = new Path(sb.toString()+"x"); try { fc.createSymlink(file, link, false); fail("Path name should be too long"); } catch (IOException x) { // Expected } } @Test /** Test symlink owner */ public void testLinkOwner() throws IOException { Path file = new Path(testBaseDir1(), "file"); Path link = new Path(testBaseDir1(), "symlinkToFile"); createAndWriteFile(file); fc.createSymlink(file, link, false); FileStatus statFile = fc.getFileStatus(file); FileStatus statLink = fc.getFileStatus(link); assertEquals(statLink.getOwner(), statFile.getOwner()); } @Test /** Test WebHdfsFileSystem.craeteSymlink(..). */ public void testWebHDFS() throws IOException { Path file = new Path(testBaseDir1(), "file"); Path link = new Path(testBaseDir1(), "linkToFile"); createAndWriteFile(file); webhdfs.createSymlink(file, link, false); fc.setReplication(link, (short)2); assertEquals(0, fc.getFileLinkStatus(link).getReplication()); assertEquals(2, fc.getFileStatus(link).getReplication()); assertEquals(2, fc.getFileStatus(file).getReplication()); } }