Java 1.4.2 File.listFiles与CIFS挂载无法正常工作 - 解决方法?

时间:2012-06-09 13:01:57

标签: java linux cifs

我正在使用Java 1.4.2和Debian 6.0.3。网络中有一个共享的Windows文件夹,它通过fstab正确安装到/mnt/share/(例如,它可以从操作系统完全可见并允许所有操作)使用CIFS。但是,当我尝试在Java中执行此操作时:

System.out.println(new File("/mnt/share/").listFiles().length)

它始终会返回0,这意味着File[]返回的listFiles为空。同样的问题适用于/mnt/share/的每个子目录。 list也返回空数组。有趣的是,其他File函数如“create”,“isDirectory”甚至“delete”都可以正常工作。从USB闪存盘(fat32)安装的目录也可以正常工作。

我在不同Windows系统的2个不同的“共享文件夹”上测试了这个;一个使用基于域的身份验证系统,另一个使用“简单共享” - 即访客访问。这种情况看起来很奇怪,因为挂载的目录应该成为文件系统的一部分,所以任何程序都可以使用它。或者至少我想过。

我想在我的程序中删除一个目录,除了listFiles上的递归遍历之外,我目前看不到其它方法,所以这个bug变得相当烦人。我能想到的唯一“解决方法”是以某种方式运行外部bash脚本,但它似乎是一个可怕的解决方案。

编辑:这似乎是特定于1.4.2的错误,在Java 6中一切正常。但是我无法迁移,所以问题仍然存在。

你能建议一些解决方法吗?最好不要切换到第三方库而不是本机库,我不能说我喜欢为单个代码行重写整个项目的想法。

2 个答案:

答案 0 :(得分:0)

由于Java 1.2有方法File.getCanonicalFile()。对于已安装目录的情况,您应该使用这样的样式:

new File("/mnt/share/").getCanonicalFile().listFiles()

答案 1 :(得分:0)

所以,两年半后放弃我遇到同样的问题,再次坚持使用1.4.2,因为我需要将代码嵌入到过时的Oracle Forms 10g版本中。

如果有人偶然遇到这个问题并决定正确地解决它,而不是破解它,那很可能与CIFS在安装远程文件系统时所做的(高度)异常inode映射有关,导致更多隐藏的错误,其中一些可以在serverfault上找到。这种映射的副作用之一是所有目录都没有硬链接计数。另一个是所有目录都具有正好为0的“大小”,而不是通常的“扇区大小或更多”,即使使用ls也可以检查。

我不能确定没有检查(专有)源代码,但我可以猜测1.5之前的Java使用了一些快捷方式,比如在内部检查链接计数而不是实际用C调用readdir(),这同样适用于任何已安装的FS。

无论如何,第二个副作用可用于在File周围创建一个简单的包装器,它不依赖于系统调用,除非它怀疑使用CIFS挂载目录。 listlistFilesjava.io.File函数的其他版本,即使是使用过滤器的函数,也会在内部依赖list(),因此可以仅覆盖它。

我不关心listFiles返回File[]而不是FileEx[]所以我没有费心去覆盖它,但应该足够简单。显然,该代码只能在具有ls命令的类Unix系统中使用。

package FSTest;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import java.util.ArrayList;

public class FileEx extends File
{
    public FileEx(String path)
    {
        super(path);
    }

    public FileEx(File f)
    {
        super(f.getAbsolutePath());
    }

    public String[] list()
    {
        if (this.canRead() && this.isDirectory())
            {
            /*
             * Checking the length of dir is not the most reliable way to distinguish CIFS mounts.
             * However, zero directory length generally indicates something unusual,
             * so calling ls on it wouldn't hurt. Ordinary directories don't suffer any overhead this way.
             * If this "zero-size" behavior is ever changed by CIFS but list() still won't work,
             * it will be safer to call super.list() first and call this.listUsingExec if returned array has 0 elements.
             * Though it might have serious performance implications, of course.
             */
            if (this.length() > 0)
                return super.list();
            else
                return this.listUsingExec();
           }
        else
            return null;
    }

    private String[] listUsingExec()
    {
        Process p;
        String command = "/bin/ls -1a " + this.getAbsolutePath();
        ArrayList list = new ArrayList();
        try
            {
            p = Runtime.getRuntime().exec(command);
            p.waitFor();
            BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
            for (String line = reader.readLine(); line != null; line = reader.readLine())
                {
                if (!line.equalsIgnoreCase(".") && !line.equalsIgnoreCase(".."))
                    list.add(line);
               }
            String[] ret = new String[list.size()];
            list.toArray(ret);
            return ret;
           }
        catch (IOException e)
            {
            return null;
           }
    }
}