[英]Java 1.4.2 File.listFiles not working properly with CIFS mounts - workaround?
我正在使用Java 1.4.2和Debian 6.0.3。 网络中有一个共享的Windows文件夹,该文件夹使用CIFS通过fstab正确安装到/mnt/share/
(例如,从OS完全可见,并允许所有操作)。 但是,当我尝试在Java中执行此操作时:
System.out.println(new File("/mnt/share/").listFiles().length)
它总是返回0
,表示listFiles
返回的File[]
为空。 相同的问题适用于/mnt/share/
每个子目录。 list
返回空数组。 足够有趣的是,其他File
功能(例如“创建”,“ isDirectory”或什至“删除”)也可以正常工作。 从USB闪存驱动器(fat32)挂载的目录也可以正常工作。
我在来自不同Windows系统的2个不同“共享文件夹”上进行了测试; 一个使用基于域的身份验证系统,另一个使用“简单共享”-即来宾访问。 这种情况似乎很奇怪,因为挂载的目录应该成为文件系统的一部分,因此任何程序都可以使用它。 至少我这样想。
我想在程序中删除目录,除递归地遍历listFiles
之外,目前没有其他方法可以执行此操作,因此此错误变得很烦人。 我唯一想到的“解决方法”是以某种方式运行外部bash脚本,但这似乎是一个糟糕的解决方案。
编辑 :似乎这是1.4.2特定的错误,在Java 6中一切正常。但是我无法迁移,因此问题仍然存在。
您能建议一些解决方法吗? 最好不要切换到第三方库而不是本地库,我不能说我喜欢出于单一代码行而重写整个项目的想法。
从Java 1.2
,提供了File.getCanonicalFile()
方法。 对于挂载目录的情况,您应该以这种方式完全使用此目录:
new File("/mnt/share/").getCanonicalFile().listFiles()
因此,在放弃两年半之后,我遇到了同样的问题,再次陷入1.4.2的困境,因为我需要将代码嵌入到过时的Oracle Forms 10g版本中。
如果有人偶然发现了这个问题并决定适当地解决它,而不是破解它,那么它很可能与CIFS在挂载远程文件系统时所做的(高度)异常的inode映射有关,从而导致了一些更模糊的错误其中可以在serverfault上找到。 这种映射的副作用之一是所有目录的硬链接数均为零。 另一个是所有目录的“大小”都完全为0,而不是通常的“扇区大小或更大”,甚至可以使用ls
进行检查。
我不能确定是否不检查(专有)源代码,但是我可以猜测到1.5之前的Java使用了一些快捷方式,例如在内部检查链接计数,而不是实际使用C调用readdir(),这对于任何已安装的FS都同样有效。
无论如何,第二个副作用可以用来围绕File创建一个简单的包装,除非怀疑使用CIFS挂载目录,否则它不会依赖系统调用。 java.io.File
其他版本的list
和listFiles
函数,甚至使用过滤器的版本,都在内部依赖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;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.