1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.hadoop.fs;
20 
21 import static org.junit.Assert.*;
22 import static org.junit.Assume.assumeTrue;
23 
24 import java.io.File;
25 import java.io.IOException;
26 
27 import org.apache.hadoop.conf.Configuration;
28 import org.junit.Before;
29 import org.junit.BeforeClass;
30 import org.junit.Test;
31 
32 public class TestFsShellCopy {
33   static Configuration conf;
34   static FsShell shell;
35   static LocalFileSystem lfs;
36   static Path testRootDir, srcPath, dstPath;
37 
38   @BeforeClass
setup()39   public static void setup() throws Exception {
40     conf = new Configuration();
41     shell = new FsShell(conf);
42     lfs = FileSystem.getLocal(conf);
43     testRootDir = lfs.makeQualified(new Path(
44         System.getProperty("test.build.data","test/build/data"),
45         "testShellCopy"));
46 
47     lfs.mkdirs(testRootDir);
48     srcPath = new Path(testRootDir, "srcFile");
49     dstPath = new Path(testRootDir, "dstFile");
50   }
51 
52   @Before
prepFiles()53   public void prepFiles() throws Exception {
54     lfs.setVerifyChecksum(true);
55     lfs.setWriteChecksum(true);
56 
57     lfs.delete(srcPath, true);
58     lfs.delete(dstPath, true);
59     FSDataOutputStream out = lfs.create(srcPath);
60     out.writeChars("hi");
61     out.close();
62     assertTrue(lfs.exists(lfs.getChecksumFile(srcPath)));
63   }
64 
65   @Test
testCopyNoCrc()66   public void testCopyNoCrc() throws Exception {
67     shellRun(0, "-get", srcPath.toString(), dstPath.toString());
68     checkPath(dstPath, false);
69   }
70 
71   @Test
testCopyCrc()72   public void testCopyCrc() throws Exception {
73     shellRun(0, "-get", "-crc", srcPath.toString(), dstPath.toString());
74     checkPath(dstPath, true);
75   }
76 
77 
78   @Test
testCorruptedCopyCrc()79   public void testCorruptedCopyCrc() throws Exception {
80     FSDataOutputStream out = lfs.getRawFileSystem().create(srcPath);
81     out.writeChars("bang");
82     out.close();
83     shellRun(1, "-get", srcPath.toString(), dstPath.toString());
84   }
85 
86   @Test
testCorruptedCopyIgnoreCrc()87   public void testCorruptedCopyIgnoreCrc() throws Exception {
88     shellRun(0, "-get", "-ignoreCrc", srcPath.toString(), dstPath.toString());
89     checkPath(dstPath, false);
90   }
91 
checkPath(Path p, boolean expectChecksum)92   private void checkPath(Path p, boolean expectChecksum) throws IOException {
93     assertTrue(lfs.exists(p));
94     boolean hasChecksum = lfs.exists(lfs.getChecksumFile(p));
95     assertEquals(expectChecksum, hasChecksum);
96   }
97 
shellRun(int n, String ... args)98   private void shellRun(int n, String ... args) throws Exception {
99     assertEquals(n, shell.run(args));
100   }
101 
102   @Test
testCopyFileFromLocal()103   public void testCopyFileFromLocal() throws Exception {
104     Path testRoot = new Path(testRootDir, "testPutFile");
105     lfs.delete(testRoot, true);
106     lfs.mkdirs(testRoot);
107 
108     Path targetDir = new Path(testRoot, "target");
109     Path filePath = new Path(testRoot, new Path("srcFile"));
110     lfs.create(filePath).close();
111     checkPut(filePath, targetDir, false);
112   }
113 
114   @Test
testCopyDirFromLocal()115   public void testCopyDirFromLocal() throws Exception {
116     Path testRoot = new Path(testRootDir, "testPutDir");
117     lfs.delete(testRoot, true);
118     lfs.mkdirs(testRoot);
119 
120     Path targetDir = new Path(testRoot, "target");
121     Path dirPath = new Path(testRoot, new Path("srcDir"));
122     lfs.mkdirs(dirPath);
123     lfs.create(new Path(dirPath, "srcFile")).close();
124     checkPut(dirPath, targetDir, false);
125   }
126 
127   @Test
testCopyFileFromWindowsLocalPath()128   public void testCopyFileFromWindowsLocalPath() throws Exception {
129     assumeTrue(Path.WINDOWS);
130     String windowsTestRootPath = (new File(testRootDir.toUri().getPath()
131         .toString())).getAbsolutePath();
132     Path testRoot = new Path(windowsTestRootPath, "testPutFile");
133     lfs.delete(testRoot, true);
134     lfs.mkdirs(testRoot);
135 
136     Path targetDir = new Path(testRoot, "target");
137     Path filePath = new Path(testRoot, new Path("srcFile"));
138     lfs.create(filePath).close();
139     checkPut(filePath, targetDir, true);
140   }
141 
142   @Test
testCopyDirFromWindowsLocalPath()143   public void testCopyDirFromWindowsLocalPath() throws Exception {
144     assumeTrue(Path.WINDOWS);
145     String windowsTestRootPath = (new File(testRootDir.toUri().getPath()
146         .toString())).getAbsolutePath();
147     Path testRoot = new Path(windowsTestRootPath, "testPutDir");
148     lfs.delete(testRoot, true);
149     lfs.mkdirs(testRoot);
150 
151     Path targetDir = new Path(testRoot, "target");
152     Path dirPath = new Path(testRoot, new Path("srcDir"));
153     lfs.mkdirs(dirPath);
154     lfs.create(new Path(dirPath, "srcFile")).close();
155     checkPut(dirPath, targetDir, true);
156   }
157 
158 
checkPut(Path srcPath, Path targetDir, boolean useWindowsPath)159   private void checkPut(Path srcPath, Path targetDir, boolean useWindowsPath)
160   throws Exception {
161     lfs.delete(targetDir, true);
162     lfs.mkdirs(targetDir);
163     lfs.setWorkingDirectory(targetDir);
164 
165     final Path dstPath = new Path("path");
166     final Path childPath = new Path(dstPath, "childPath");
167     lfs.setWorkingDirectory(targetDir);
168 
169     // copy to new file, then again
170     prepPut(dstPath, false, false);
171     checkPut(0, srcPath, dstPath, useWindowsPath);
172     if (lfs.isFile(srcPath)) {
173       checkPut(1, srcPath, dstPath, useWindowsPath);
174     } else { // directory works because it copies into the dir
175       // clear contents so the check won't think there are extra paths
176       prepPut(dstPath, true, true);
177       checkPut(0, srcPath, dstPath, useWindowsPath);
178     }
179 
180     // copy to non-existent subdir
181     prepPut(childPath, false, false);
182     checkPut(1, srcPath, dstPath, useWindowsPath);
183 
184     // copy into dir, then with another name
185     prepPut(dstPath, true, true);
186     checkPut(0, srcPath, dstPath, useWindowsPath);
187     prepPut(childPath, true, true);
188     checkPut(0, srcPath, childPath, useWindowsPath);
189 
190     // try to put to pwd with existing dir
191     prepPut(targetDir, true, true);
192     checkPut(0, srcPath, null, useWindowsPath);
193     prepPut(targetDir, true, true);
194     checkPut(0, srcPath, new Path("."), useWindowsPath);
195 
196     // try to put to pwd with non-existent cwd
197     prepPut(dstPath, false, true);
198     lfs.setWorkingDirectory(dstPath);
199     checkPut(1, srcPath, null, useWindowsPath);
200     prepPut(dstPath, false, true);
201     checkPut(1, srcPath, new Path("."), useWindowsPath);
202   }
203 
prepPut(Path dst, boolean create, boolean isDir)204   private void prepPut(Path dst, boolean create,
205                        boolean isDir) throws IOException {
206     lfs.delete(dst, true);
207     assertFalse(lfs.exists(dst));
208     if (create) {
209       if (isDir) {
210         lfs.mkdirs(dst);
211         assertTrue(lfs.isDirectory(dst));
212       } else {
213         lfs.mkdirs(new Path(dst.getName()));
214         lfs.create(dst).close();
215         assertTrue(lfs.isFile(dst));
216       }
217     }
218   }
219 
checkPut(int exitCode, Path src, Path dest, boolean useWindowsPath)220   private void checkPut(int exitCode, Path src, Path dest,
221       boolean useWindowsPath) throws Exception {
222     String argv[] = null;
223     String srcPath = src.toString();
224     if (useWindowsPath) {
225       srcPath = (new File(srcPath)).getAbsolutePath();
226     }
227     if (dest != null) {
228       argv = new String[]{ "-put", srcPath, pathAsString(dest) };
229     } else {
230       argv = new String[]{ "-put", srcPath };
231       dest = new Path(Path.CUR_DIR);
232     }
233 
234     Path target;
235     if (lfs.exists(dest)) {
236       if (lfs.isDirectory(dest)) {
237         target = new Path(pathAsString(dest), src.getName());
238       } else {
239         target = dest;
240       }
241     } else {
242       target = new Path(lfs.getWorkingDirectory(), dest);
243     }
244     boolean targetExists = lfs.exists(target);
245     Path parent = lfs.makeQualified(target).getParent();
246 
247     System.out.println("COPY src["+src.getName()+"] -> ["+dest+"] as ["+target+"]");
248     String lsArgv[] = new String[]{ "-ls", "-R", pathAsString(parent) };
249     shell.run(lsArgv);
250 
251     int gotExit = shell.run(argv);
252 
253     System.out.println("copy exit:"+gotExit);
254     lsArgv = new String[]{ "-ls", "-R", pathAsString(parent) };
255     shell.run(lsArgv);
256 
257     if (exitCode == 0) {
258       assertTrue(lfs.exists(target));
259       assertTrue(lfs.isFile(src) == lfs.isFile(target));
260       assertEquals(1, lfs.listStatus(lfs.makeQualified(target).getParent()).length);
261     } else {
262       assertEquals(targetExists, lfs.exists(target));
263     }
264     assertEquals(exitCode, gotExit);
265   }
266 
267   @Test
testRepresentsDir()268   public void testRepresentsDir() throws Exception {
269     Path subdirDstPath = new Path(dstPath, srcPath.getName());
270     String argv[] = null;
271     lfs.delete(dstPath, true);
272     assertFalse(lfs.exists(dstPath));
273 
274     argv = new String[]{ "-put", srcPath.toString(), dstPath.toString() };
275     assertEquals(0, shell.run(argv));
276     assertTrue(lfs.exists(dstPath) && lfs.isFile(dstPath));
277 
278     lfs.delete(dstPath, true);
279     assertFalse(lfs.exists(dstPath));
280 
281     // since dst path looks like a dir, it should not copy the file and
282     // rename it to what looks like a directory
283     lfs.delete(dstPath, true); // make copy fail
284     for (String suffix : new String[]{ "/", "/." } ) {
285       argv = new String[]{
286           "-put", srcPath.toString(), dstPath.toString()+suffix };
287       assertEquals(1, shell.run(argv));
288       assertFalse(lfs.exists(dstPath));
289       assertFalse(lfs.exists(subdirDstPath));
290     }
291 
292     // since dst path looks like a dir, it should not copy the file and
293     // rename it to what looks like a directory
294     for (String suffix : new String[]{ "/", "/." } ) {
295       // empty out the directory and create to make copy succeed
296       lfs.delete(dstPath, true);
297       lfs.mkdirs(dstPath);
298       argv = new String[]{
299           "-put", srcPath.toString(), dstPath.toString()+suffix };
300       assertEquals(0, shell.run(argv));
301       assertTrue(lfs.exists(subdirDstPath));
302       assertTrue(lfs.isFile(subdirDstPath));
303     }
304 
305     // ensure .. is interpreted as a dir
306     String dotdotDst = dstPath+"/foo/..";
307     lfs.delete(dstPath, true);
308     lfs.mkdirs(new Path(dstPath, "foo"));
309     argv = new String[]{ "-put", srcPath.toString(), dotdotDst };
310     assertEquals(0, shell.run(argv));
311     assertTrue(lfs.exists(subdirDstPath));
312     assertTrue(lfs.isFile(subdirDstPath));
313   }
314 
315   @Test
testCopyMerge()316   public void testCopyMerge() throws Exception {
317     Path root = new Path(testRootDir, "TestMerge");
318     Path f1 = new Path(root, "f1");
319     Path f2 = new Path(root, "f2");
320     Path f3 = new Path(root, "f3");
321     Path fnf = new Path(root, "fnf");
322     Path d = new Path(root, "dir");
323     Path df1 = new Path(d, "df1");
324     Path df2 = new Path(d, "df2");
325     Path df3 = new Path(d, "df3");
326 
327     createFile(f1, f2, f3, df1, df2, df3);
328 
329     int exit;
330     // one file, kind of silly
331     exit = shell.run(new String[]{
332         "-getmerge",
333         f1.toString(),
334         "out" });
335     assertEquals(0, exit);
336     assertEquals("f1", readFile("out"));
337 
338     exit = shell.run(new String[]{
339         "-getmerge",
340         fnf.toString(),
341         "out" });
342     assertEquals(1, exit);
343     assertFalse(lfs.exists(new Path("out")));
344 
345     // two files
346     exit = shell.run(new String[]{
347         "-getmerge",
348         f1.toString(), f2.toString(),
349         "out" });
350     assertEquals(0, exit);
351     assertEquals("f1f2", readFile("out"));
352 
353     // two files, preserves order
354     exit = shell.run(new String[]{
355         "-getmerge",
356         f2.toString(), f1.toString(),
357         "out" });
358     assertEquals(0, exit);
359     assertEquals("f2f1", readFile("out"));
360 
361     // two files
362     exit = shell.run(new String[]{
363         "-getmerge", "-nl",
364         f1.toString(), f2.toString(),
365         "out" });
366     assertEquals(0, exit);
367     assertEquals("f1\nf2\n", readFile("out"));
368 
369     // glob three files
370     shell.run(new String[]{
371         "-getmerge", "-nl",
372         new Path(root, "f*").toString(),
373         "out" });
374     assertEquals(0, exit);
375     assertEquals("f1\nf2\nf3\n", readFile("out"));
376 
377     // directory with 3 files, should skip subdir
378     shell.run(new String[]{
379         "-getmerge", "-nl",
380         root.toString(),
381         "out" });
382     assertEquals(0, exit);
383     assertEquals("f1\nf2\nf3\n", readFile("out"));
384 
385     // subdir
386     shell.run(new String[]{
387         "-getmerge", "-nl",
388         d.toString(), "out"});
389     assertEquals(0, exit);
390     assertEquals("df1\ndf2\ndf3\n", readFile("out"));
391 
392     // file, dir, file
393     shell.run(new String[]{
394         "-getmerge", "-nl",
395         f1.toString(), d.toString(), f2.toString(), "out" });
396     assertEquals(0, exit);
397     assertEquals("f1\ndf1\ndf2\ndf3\nf2\n", readFile("out"));
398   }
399 
400 
401   @Test
testMoveFileFromLocal()402   public void testMoveFileFromLocal() throws Exception {
403     Path testRoot = new Path(testRootDir, "testPutFile");
404     lfs.delete(testRoot, true);
405     lfs.mkdirs(testRoot);
406 
407     Path target = new Path(testRoot, "target");
408     Path srcFile = new Path(testRoot, new Path("srcFile"));
409     lfs.createNewFile(srcFile);
410 
411     int exit = shell.run(new String[]{
412         "-moveFromLocal", srcFile.toString(), target.toString() });
413     assertEquals(0, exit);
414     assertFalse(lfs.exists(srcFile));
415     assertTrue(lfs.exists(target));
416     assertTrue(lfs.isFile(target));
417   }
418 
419   @Test
testMoveDirFromLocal()420   public void testMoveDirFromLocal() throws Exception {
421     Path testRoot = new Path(testRootDir, "testPutDir");
422     lfs.delete(testRoot, true);
423     lfs.mkdirs(testRoot);
424 
425     Path srcDir = new Path(testRoot, "srcDir");
426     lfs.mkdirs(srcDir);
427     Path targetDir = new Path(testRoot, "target");
428 
429     int exit = shell.run(new String[]{
430         "-moveFromLocal", srcDir.toString(), targetDir.toString() });
431     assertEquals(0, exit);
432     assertFalse(lfs.exists(srcDir));
433     assertTrue(lfs.exists(targetDir));
434   }
435 
436   @Test
testMoveDirFromLocalDestExists()437   public void testMoveDirFromLocalDestExists() throws Exception {
438     Path testRoot = new Path(testRootDir, "testPutDir");
439     lfs.delete(testRoot, true);
440     lfs.mkdirs(testRoot);
441 
442     Path srcDir = new Path(testRoot, "srcDir");
443     lfs.mkdirs(srcDir);
444     Path targetDir = new Path(testRoot, "target");
445     lfs.mkdirs(targetDir);
446 
447     int exit = shell.run(new String[]{
448         "-moveFromLocal", srcDir.toString(), targetDir.toString() });
449     assertEquals(0, exit);
450     assertFalse(lfs.exists(srcDir));
451     assertTrue(lfs.exists(new Path(targetDir, srcDir.getName())));
452 
453     lfs.mkdirs(srcDir);
454     exit = shell.run(new String[]{
455         "-moveFromLocal", srcDir.toString(), targetDir.toString() });
456     assertEquals(1, exit);
457     assertTrue(lfs.exists(srcDir));
458   }
459 
460   @Test
testMoveFromWindowsLocalPath()461   public void testMoveFromWindowsLocalPath() throws Exception {
462     assumeTrue(Path.WINDOWS);
463     Path testRoot = new Path(testRootDir, "testPutFile");
464     lfs.delete(testRoot, true);
465     lfs.mkdirs(testRoot);
466 
467     Path target = new Path(testRoot, "target");
468     Path srcFile = new Path(testRoot, new Path("srcFile"));
469     lfs.createNewFile(srcFile);
470 
471     String winSrcFile = (new File(srcFile.toUri().getPath()
472         .toString())).getAbsolutePath();
473     shellRun(0, "-moveFromLocal", winSrcFile, target.toString());
474     assertFalse(lfs.exists(srcFile));
475     assertTrue(lfs.exists(target));
476     assertTrue(lfs.isFile(target));
477   }
478 
479   @Test
testGetWindowsLocalPath()480   public void testGetWindowsLocalPath() throws Exception {
481     assumeTrue(Path.WINDOWS);
482     String winDstFile = (new File(dstPath.toUri().getPath()
483         .toString())).getAbsolutePath();
484     shellRun(0, "-get", srcPath.toString(), winDstFile);
485     checkPath(dstPath, false);
486   }
487 
createFile(Path .... paths)488   private void createFile(Path ... paths) throws IOException {
489     for (Path path : paths) {
490       FSDataOutputStream out = lfs.create(path);
491       out.write(path.getName().getBytes());
492       out.close();
493     }
494   }
495 
readFile(String out)496   private String readFile(String out) throws IOException {
497     Path path = new Path(out);
498     FileStatus stat = lfs.getFileStatus(path);
499     FSDataInputStream in = lfs.open(path);
500     byte[] buffer = new byte[(int)stat.getLen()];
501     in.readFully(buffer);
502     in.close();
503     lfs.delete(path, false);
504     return new String(buffer);
505   }
506 
507   // path handles "." rather oddly
pathAsString(Path p)508   private String pathAsString(Path p) {
509     String s = (p == null) ? Path.CUR_DIR : p.toString();
510     return s.isEmpty() ? Path.CUR_DIR : s;
511   }
512 }
513