From cbbb53e06e385c592918cc007889f8cea4585041 Mon Sep 17 00:00:00 2001 From: Martin Oberhuber < martin.oberhuber@windriver.com> Date: Thu, 6 Aug 2009 21:37:50 +0000 Subject: [PATCH] [285226] Empty directory shown as an error message --- releng/org.eclipse.rse.updatesite/site.xml | 4 +- .../org.eclipse.rse.ssh-feature/feature.xml | 2 +- .../META-INF/MANIFEST.MF | 2 +- .../services/ssh/files/SftpFileService.java | 39 +++- .../org.eclipse.rse.tests-feature/feature.xml | 2 +- .../subsystems/files/FileServiceTest.java | 211 +++++++++++++++++- 6 files changed, 243 insertions(+), 17 deletions(-) diff --git a/releng/org.eclipse.rse.updatesite/site.xml b/releng/org.eclipse.rse.updatesite/site.xml index 1e187ce70f5..e31649a6a55 100644 --- a/releng/org.eclipse.rse.updatesite/site.xml +++ b/releng/org.eclipse.rse.updatesite/site.xml @@ -596,7 +596,7 @@ - + @@ -608,7 +608,7 @@ - + diff --git a/rse/features/org.eclipse.rse.ssh-feature/feature.xml b/rse/features/org.eclipse.rse.ssh-feature/feature.xml index da7027b2cf6..1db1d4d7903 100644 --- a/rse/features/org.eclipse.rse.ssh-feature/feature.xml +++ b/rse/features/org.eclipse.rse.ssh-feature/feature.xml @@ -12,7 +12,7 @@ diff --git a/rse/plugins/org.eclipse.rse.services.ssh/META-INF/MANIFEST.MF b/rse/plugins/org.eclipse.rse.services.ssh/META-INF/MANIFEST.MF index b27e8d3428b..14c854871b8 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/META-INF/MANIFEST.MF +++ b/rse/plugins/org.eclipse.rse.services.ssh/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.rse.services.ssh;singleton:=true -Bundle-Version: 3.0.0.qualifier +Bundle-Version: 3.0.1.qualifier Bundle-Activator: org.eclipse.rse.internal.services.ssh.Activator Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/internal/services/ssh/files/SftpFileService.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/internal/services/ssh/files/SftpFileService.java index 91d8a4ea229..90cfddc7139 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/internal/services/ssh/files/SftpFileService.java +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/internal/services/ssh/files/SftpFileService.java @@ -39,6 +39,7 @@ * David McKnight (IBM) - [271244] [sftp files] "My Home" filter not working * David McKnight (IBM) - [272882] [api] Handle exceptions in IService.initService() * Martin Oberhuber (Wind River) - [274568] Dont use SftpMonitor for Streams transfer + * Patrick Tassé (Ericsson) - [285226] Empty directory shown as an error message *******************************************************************************/ package org.eclipse.rse.internal.services.ssh.files; @@ -87,6 +88,7 @@ import org.eclipse.rse.services.clientserver.messages.SystemMessage; import org.eclipse.rse.services.clientserver.messages.SystemMessageException; import org.eclipse.rse.services.clientserver.messages.SystemOperationCancelledException; import org.eclipse.rse.services.clientserver.messages.SystemOperationFailedException; +import org.eclipse.rse.services.clientserver.messages.SystemRemoteSecurityException; import org.eclipse.rse.services.clientserver.messages.SystemUnexpectedErrorException; import org.eclipse.rse.services.files.AbstractFileService; import org.eclipse.rse.services.files.HostFilePermissions; @@ -95,8 +97,8 @@ import org.eclipse.rse.services.files.IFileService; import org.eclipse.rse.services.files.IHostFile; import org.eclipse.rse.services.files.IHostFilePermissions; import org.eclipse.rse.services.files.IHostFilePermissionsContainer; +import org.eclipse.rse.services.files.RemoteFileException; import org.eclipse.rse.services.files.RemoteFileIOException; -import org.eclipse.rse.services.files.RemoteFileSecurityException; public class SftpFileService extends AbstractFileService implements ISshService, IFilePermissionsService { @@ -438,7 +440,7 @@ public class SftpFileService extends AbstractFileService implements ISshService, SystemMessageException messageException; SftpException sftpe = (SftpException)e; if (sftpe.id == ChannelSftp.SSH_FX_PERMISSION_DENIED) { - messageException = new RemoteFileSecurityException(e); + messageException = new SystemRemoteSecurityException(Activator.PLUGIN_ID, e.getLocalizedMessage(), e); } else if (sftpe.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { //TODO better throw SENFE at the place where we know what element and operation is done messageException = new SystemElementNotFoundException("", ""); //$NON-NLS-1$ //$NON-NLS-2$ @@ -548,6 +550,15 @@ public class SftpFileService extends AbstractFileService implements ISshService, //don't show the trivial names continue; } + if (vv.size() == 1 && fileName.equals(parentPath.substring(parentPath.lastIndexOf('/') + 1))) { + //If there is only one entry and it has the same name as the parent path, the parentPath could be a file. + //Check if the parentPath is a directory. + SftpATTRS attrs = getChannel("SftpFileService.internalFetch: " + parentPath).stat(parentPath); //$NON-NLS-1$ + if (!attrs.isDir()) { + // parent was a file and not a folder + throw new RemoteFileException("Not a folder: " + parentPath); //$NON-NLS-1$ + } + } if (filematcher.matches(fileName) || (lsEntry.getAttrs().isDir() && fileType!=IFileService.FILE_TYPE_FOLDERS)) { //get ALL directory names (unless looking for folders only) SftpHostFile node = makeHostFile(parentPath, fileName, lsEntry.getAttrs()); @@ -560,6 +571,22 @@ public class SftpFileService extends AbstractFileService implements ISshService, } Activator.trace("SftpFileService.internalFetch <--"); //$NON-NLS-1$ } catch(Exception e) { + if ( (e instanceof SftpException) && ((SftpException)e).id==ChannelSftp.SSH_FX_NO_SUCH_FILE) { + //We can get a "2: No such file" exception when the directory is empty. + try { + // Check if the parentPath exists and is a folder + SftpATTRS attrs = getChannel("SftpFileService.internalFetch: " + parentPath).stat(parentPath); //$NON-NLS-1$ + if (attrs.isDir()) { + // We MUST NOT throw an exception here. Just return + // an empty IHostFile array. + return new IHostFile[0]; + } + // else, fall through to exception handling -- will send + // SystemRemoteFileIOException + } catch (Exception ee) { + //Can't get the folder attributes so let the first exception be handled. + } + } //TODO throw new SystemMessageException. //We get a "2: No such file" exception when we try to get contents //of a symbolic link that turns out to point to a file rather than @@ -881,7 +908,7 @@ public class SftpFileService extends AbstractFileService implements ISshService, public IHostFile[] getRoots(IProgressMonitor monitor) { IHostFile root = null; try { - root = getFile(null, "/", monitor); + root = getFile(null, "/", monitor); //$NON-NLS-1$ } catch (SystemMessageException e){ } @@ -957,7 +984,7 @@ public class SftpFileService extends AbstractFileService implements ISshService, try { getChannel("SftpFileService.delete.rm").rm(fullPathRecoded); //$NON-NLS-1$ } catch (Exception e2) { - throw new SystemElementNotFoundException(Activator.PLUGIN_ID, fullPath, "delete"); + throw new SystemElementNotFoundException(Activator.PLUGIN_ID, fullPath, "delete"); //$NON-NLS-1$ } } else { //Security exception, or similar: will be wrapped in makeSystemMessageException() @@ -1184,7 +1211,7 @@ public class SftpFileService extends AbstractFileService implements ISshService, } catch (Exception e) { Activator.trace("SftpFileService.setReadOnly "+path+" failed: "+e.toString()); //$NON-NLS-1$ //$NON-NLS-2$ if ((e instanceof SftpException) && ((SftpException) e).id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { - throw new SystemElementNotFoundException(Activator.PLUGIN_ID, path, "setReadOnly"); + throw new SystemElementNotFoundException(Activator.PLUGIN_ID, path, "setReadOnly"); //$NON-NLS-1$ } throw makeSystemMessageException(e); } finally { @@ -1298,7 +1325,7 @@ public class SftpFileService extends AbstractFileService implements ISshService, } catch (Exception e) { Activator.trace("SftpFileService.setFilePermissions " + path + " failed: " + e.toString()); //$NON-NLS-1$ //$NON-NLS-2$ if ((e instanceof SftpException) && ((SftpException) e).id == ChannelSftp.SSH_FX_NO_SUCH_FILE) { - throw new SystemElementNotFoundException(Activator.PLUGIN_ID, path, "setFilePermissions"); + throw new SystemElementNotFoundException(Activator.PLUGIN_ID, path, "setFilePermissions"); //$NON-NLS-1$ } throw makeSystemMessageException(e); } finally { diff --git a/rse/tests/org.eclipse.rse.tests-feature/feature.xml b/rse/tests/org.eclipse.rse.tests-feature/feature.xml index c79f9473ac1..83ce051c1e2 100644 --- a/rse/tests/org.eclipse.rse.tests-feature/feature.xml +++ b/rse/tests/org.eclipse.rse.tests-feature/feature.xml @@ -12,7 +12,7 @@ diff --git a/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/subsystems/files/FileServiceTest.java b/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/subsystems/files/FileServiceTest.java index 5c9995e4ebf..de0a6f95627 100644 --- a/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/subsystems/files/FileServiceTest.java +++ b/rse/tests/org.eclipse.rse.tests/src/org/eclipse/rse/tests/subsystems/files/FileServiceTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2008 Wind River Systems, Inc. and others. + * Copyright (c) 2006, 2009 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,6 +12,7 @@ * Martin Oberhuber (Wind River) - [186640] Add IRSESystemType.testProperty() * Martin Oberhuber (Wind River) - organize, enable and tag test cases * Martin Oberhuber (Wind River) - [235360][ftp][ssh] Return proper "Root" IHostFile + * Patrick Tassé (Ericsson) - [285226] Empty directory shown as an error message *******************************************************************************/ package org.eclipse.rse.tests.subsystems.files; @@ -24,13 +25,22 @@ import junit.framework.TestSuite; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.services.IService; +import org.eclipse.rse.services.clientserver.PathUtility; +import org.eclipse.rse.services.clientserver.messages.SystemElementNotFoundException; import org.eclipse.rse.services.clientserver.messages.SystemMessageException; import org.eclipse.rse.services.files.IFileService; import org.eclipse.rse.services.files.IHostFile; +import org.eclipse.rse.services.files.RemoteFileException; +import org.eclipse.rse.services.shells.IHostOutput; +import org.eclipse.rse.services.shells.IHostShell; +import org.eclipse.rse.services.shells.IShellService; import org.eclipse.rse.subsystems.files.core.model.RemoteFileUtility; import org.eclipse.rse.subsystems.files.core.servicesubsystem.IFileServiceSubSystem; import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFile; import org.eclipse.rse.tests.core.connection.RSEBaseConnectionTestCase; +import org.eclipse.rse.tests.subsystems.shells.ShellOutputListener; public class FileServiceTest extends RSEBaseConnectionTestCase { @@ -75,7 +85,8 @@ public class FileServiceTest extends RSEBaseConnectionTestCase { TestSuite suite = new TestSuite(baseName); // // Add a test suite for each connection type - String[] connTypes = { "local", "ssh", "ftpWindows", "ftp", "linux", "windows" }; + String[] connTypes = { "local", "ssh", "ftpWindows", "ftpSsh", "linux", "windows", "unix" }; + //String[] connTypes = { "unix" }; //String[] connTypes = { "local" }; // String[] connTypes = { "ssh" }; @@ -121,7 +132,12 @@ public class FileServiceTest extends RSEBaseConnectionTestCase { //Return a filename for testing that exposes all characters valid on the file system if (!isWindows()) { //UNIX TODO: test embedded newlines - return "a !@#${a}\"\' fi\tle\b\\%^&*()?_ =[]~+-'`;:,.|<>"; //$NON-NLS-1$ + String testName = "a !@#${a}\"\' fi\tle\b\\%^&*()?_ =[]~+-'`;:,.|<>"; //$NON-NLS-1$ + // Bug 235492: DStore is designed to treat '\' and '/' the same way, so do not + // test this. + if (fss.getSubSystemConfiguration().getServiceImplType().getName().equals("org.eclipse.rse.services.dstore.IDStoreService")) { //$NON-NLS-1$ + testName.replace('\\', ' '); + } } //Fallback: Windows TODO: test unicode //Note: The trailing dot ('.') is really unfair on Windows because the file @@ -130,6 +146,32 @@ public class FileServiceTest extends RSEBaseConnectionTestCase { return "a !@#${a}'` file%^&()_ =[]~+-;.,"; //$NON-NLS-1$ } + /** + * Find the first IShellServiceSubSystem service associated with the host. + * + * @return shell service subsystem, or null if not found. + */ + public IShellService getRelatedShellService() { + IHost host = fss.getHost(); + ISubSystem[] subSystems = host.getSubSystems(); + IShellService ssvc = null; + for (int i = 0; subSystems != null && i < subSystems.length; i++) { + IService svc = subSystems[i].getSubSystemConfiguration().getService(host); + if (svc != null) { + ssvc = (IShellService) svc.getAdapter(IShellService.class); + if (ssvc != null) { + try { + subSystems[i].checkIsConnected(getDefaultProgressMonitor()); + return ssvc; + } catch (SystemMessageException e) { + e.printStackTrace(); + } + } + } + } + return null; + } + public void testGetRootProperties() throws Exception { //-test-author-:MartinOberhuber if (isTestDisabled()) return; @@ -139,11 +181,10 @@ public class FileServiceTest extends RSEBaseConnectionTestCase { for (int i = 0; i < roots.length; i++) { assertTrue(roots[i].isRoot()); assertTrue(roots[i].exists()); - assertNull(roots[i].getParentPath()); //dstore: bug 235471 + assertNull(roots[i].getParentPath()); String rootName = roots[i].getName(); assertNotNull(rootName); System.out.println(rootName); - // DStore: NPE, bug 240710 IHostFile newHf = fs.getFile(null, rootName, new NullProgressMonitor()); assertTrue(newHf.isRoot()); assertTrue(newHf.exists()); @@ -175,7 +216,7 @@ public class FileServiceTest extends RSEBaseConnectionTestCase { if (isTestDisabled()) return; String testName = getTestFileName(); - IHostFile hf = fs.createFile(tempDirPath, testName, mon); //dstore-linux: bug 235492 + IHostFile hf = fs.createFile(tempDirPath, testName, mon); assertTrue(hf.exists()); assertTrue(hf.canRead()); assertTrue(hf.canWrite()); @@ -231,4 +272,162 @@ public class FileServiceTest extends RSEBaseConnectionTestCase { } } + public String[] runRemoteCommand(IShellService shellService, String workingDirectory, String cmd) throws SystemMessageException, InterruptedException { + IHostShell hostShell = null; + hostShell = shellService.runCommand(workingDirectory, cmd, null, mon); + ShellOutputListener outputListener = new ShellOutputListener(); + hostShell.addOutputListener(outputListener); + hostShell.writeToShell("exit"); + assertNotNull(hostShell); + assertNotNull(hostShell.getStandardOutputReader()); + while (hostShell.isActive()) { + Thread.sleep(1000); + } + Object[] allOutput = outputListener.getAllOutput(); + if (allOutput!=null) { + String[] sOutput = new String[allOutput.length]; + for (int i = 0; i < allOutput.length; i++) { + if (allOutput[i] instanceof IHostOutput) { + sOutput[i] = ((IHostOutput) allOutput[i]).getString(); + } else { + sOutput[i] = allOutput[i].toString(); + } + } + return sOutput; + } + return null; + } + + /** + * Create a symbolic link in the context of tempDirPath. + * + * @param source source file to link from + * @param target target file to link to + * @return true if link was successfully created. + */ + public boolean mkSymLink(String source, String target) { + if (!fss.getHost().getSystemType().isWindows()) { + IShellService ss = getRelatedShellService(); + if (ss != null) { + String[] allOutput; + try { + String src = PathUtility.enQuoteUnix(source); + String tgt = PathUtility.enQuoteUnix(target); + String cmd = "ln -s " + src + " " + tgt; + allOutput = runRemoteCommand(ss, tempDirPath, cmd); + IHostFile hf = fs.getFile(tempDirPath, target, mon); + if (hf.exists()) { + return true; + } + allOutput = new String[] { "Failed to symlink: " + cmd }; + } catch (Exception e) { + allOutput = new String[] { "Exception thrown: " + e }; + } + System.out.println("WARNING: Could not create symlink"); + if (allOutput != null) { + for (int i = 0; i < allOutput.length; i++) { + System.out.println(allOutput[i]); + } + } + } + } + return false; + } + + public void testListEmptyFolder() throws SystemMessageException { + // -test-author-:PatrickTassé + if (isTestDisabled()) return; + + String testName = "empty"; + IHostFile hf = fs.createFolder(tempDirPath, testName, mon); + assertTrue(hf.exists()); + assertTrue(hf.isDirectory()); + assertTrue(hf.canRead()); + assertTrue(hf.canWrite()); + assertEquals(testName, hf.getName()); + assertEquals(tempDirPath, hf.getParentPath()); + long modDate = hf.getModifiedDate(); + assertTrue(modDate > 0); + if (fss.getHost().getSystemType().isLocal()) { + File theFile = new File(remoteTempDir.getAbsolutePath(), testName); + assertTrue(theFile.exists()); + assertTrue(modDate == theFile.lastModified()); + } + IHostFile[] hfa = fs.list(hf.getAbsolutePath(), "*", IFileService.FILE_TYPE_FILES_AND_FOLDERS, mon); + assertEquals(0, hfa.length); + // check for symlink-to-empty-folder case + if (mkSymLink(testName, "lto" + testName)) { + IHostFile hf2 = fs.getFile(tempDirPath, "lto" + testName, mon); + assertTrue(hf2.isDirectory()); + hfa = fs.list(hf2.getAbsolutePath(), "*", IFileService.FILE_TYPE_FILES_AND_FOLDERS, mon); + assertEquals(0, hfa.length); + } + } + + public void testListNonExistentFolder() throws SystemMessageException, InterruptedException { + // -test-author-:PatrickTassé + if (isTestDisabled()) return; + + String testPath = tempDirPath + "/non/existent"; + try { + IHostFile[] hfa = fs.list(testPath, "*", IFileService.FILE_TYPE_FILES_AND_FOLDERS, mon); + // Bug 285942: LocalFileService and DStoreFileService return empty array today + // Assert something impossible since an exception is expected + assertEquals("Exception expected on list nonexistent", -1, hfa.length); + } catch (SystemMessageException e) { + assertTrue(e instanceof SystemElementNotFoundException); + } + // check for symlink-to-non-existent case + if (mkSymLink("non/existent", "ltononex")) { + IHostFile hf2 = fs.getFile(tempDirPath, "ltononex", mon); + try { + IHostFile[] hfa = fs.list(hf2.getAbsolutePath(), "*", IFileService.FILE_TYPE_FILES_AND_FOLDERS, mon); + assertEquals("Exception expected on list broken symlink", -1, hfa.length); + } catch (SystemMessageException e) { + assertTrue(e instanceof SystemElementNotFoundException); + } + } + } + + public void testListNotAFolder() throws SystemMessageException { + // -test-author-:PatrickTassé + if (isTestDisabled()) return; + + String testName = getTestFileName(); + IHostFile hf = fs.createFile(tempDirPath, testName, mon); + assertTrue(hf.exists()); + assertTrue(hf.canRead()); + assertTrue(hf.canWrite()); + assertEquals(hf.getName(), testName); + assertEquals(hf.getParentPath(), tempDirPath); + assertEquals(hf.getSize(), 0); + long modDate = hf.getModifiedDate(); + assertTrue(modDate > 0); + if (fss.getHost().getSystemType().isLocal()) { + File theFile = new File(remoteTempDir.getAbsolutePath(), testName); + assertTrue(theFile.exists()); + assertTrue(modDate == theFile.lastModified()); + } + try { + IHostFile[] hfa = fs.list(hf.getAbsolutePath(), "*", IFileService.FILE_TYPE_FILES_AND_FOLDERS, mon); + // Bug 285942: LocalFileService and DStoreFileService return empty array today + // Assert something impossible since an exception is expected + assertEquals("Exception expected on list not-a-folder", -1, hfa.length); + } catch (SystemMessageException e) { + assertTrue(e instanceof RemoteFileException); + } + // check for symlink-to-not-a-folder case + if (mkSymLink(testName, "lto" + testName)) { + try { + IHostFile hf2 = fs.getFile(tempDirPath, "lto" + testName, mon); + assertTrue(hf2.isFile()); + IHostFile[] hfa = fs.list(hf2.getAbsolutePath(), "*", IFileService.FILE_TYPE_FILES_AND_FOLDERS, mon); + // Assert something impossible since an exception is expected + assertEquals("Exception expected on list symlink-to-folder", -1, hfa.length); + } catch (SystemMessageException e) { + assertTrue(e instanceof RemoteFileException); + } + } + } + }