| /** |
| * 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 java.io.*; |
| import java.net.URI; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileStatus; |
| import org.apache.hadoop.fs.FileContext; |
| import org.apache.hadoop.fs.Path; |
| 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.FSConstants; |
| import static org.apache.hadoop.fs.FileContextTestHelper.*; |
| import org.apache.hadoop.ipc.RemoteException; |
| import static org.junit.Assert.*; |
| import org.junit.Test; |
| import org.junit.BeforeClass; |
| import org.junit.AfterClass; |
| |
| /** |
| * Test symbolic links using FileContext and Hdfs. |
| */ |
| public class TestFcHdfsSymlink extends FileContextSymlinkBaseTest { |
| |
| private static MiniDFSCluster cluster; |
| |
| 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_PERMISSIONS_ENABLED_KEY, true); |
| conf.set(FsPermission.UMASK_LABEL, "000"); |
| cluster = new MiniDFSCluster.Builder(conf).build(); |
| fc = FileContext.getFileContext(cluster.getURI(0)); |
| } |
| |
| @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 = FSConstants.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 stat_file = fc.getFileStatus(file); |
| FileStatus stat_link = fc.getFileStatus(link); |
| assertEquals(stat_link.getOwner(), stat_file.getOwner()); |
| } |
| } |