【发布时间】:2012-01-15 19:53:42
【问题描述】:
我正在尝试使用 JNA 创建与 FUSE 库的绑定,但我在路上遇到了障碍。我已经尽可能地把代码最小化,让它在这里易于消化。
FUSE 库附带了一些用 C 编写的示例文件系统。其中最简单的是 hello.c。以下是其代码的最小化版本,仅在文件系统函数中进行了一些打印:
hello.c:
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
gcc -Wall hello.c -o hello `pkg-config fuse --cflags --libs`
*/
#define FUSE_USE_VERSION 26
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
static int hello_getattr(const char *path, struct stat *stbuf)
{
printf("getattr was called\n");
return 0;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
printf("readdir was called\n");
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
printf("open was called\n");
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
printf("read was called\n");
return 0;
}
static struct fuse_operations hello_oper = {
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
int main(int argc, char *argv[])
{
return fuse_main_real(argc, argv, &hello_oper, sizeof(hello_oper), NULL);
}
这可以使用gcc -Wall hello.c -o hello -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse -pthread -lfuse -lrt -ldl编译
并使用./hello.c -f /some/mount/point 调用
-f 标志是让它留在前台,以便您可以看到printf() 的工作。
所有这些都运行良好,您可以看到printf() 正常执行。我正在尝试使用 JNA 在 Java 中复制相同的内容。这是我想出的:
FuseTemp.java:
import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public class FuseTemp
{
public static interface Fuse extends Library
{
int fuse_main_real(int argc, String[] argv, StructFuseOperations op, long size, Pointer user_data);
}
@SuppressWarnings("unused")
public static class StructFuseOperations extends Structure
{
public static class ByReference extends StructFuseOperations implements Structure.ByReference
{
}
public Callback getattr = new Callback()
{
public int callback(final String path, final Pointer stat)
{
System.out.println("getattr was called");
return 0;
}
};
public Callback readlink = null;
public Callback mknod = null;
public Callback mkdir = null;
public Callback unlink = null;
public Callback rmdir = null;
public Callback symlink = null;
public Callback rename = null;
public Callback link = null;
public Callback chmod = null;
public Callback chown = null;
public Callback truncate = null;
public Callback utime = null;
public Callback open = new Callback()
{
public int callback(final String path, final Pointer info)
{
System.out.println("open was called");
return 0;
}
};
public Callback read = new Callback()
{
public int callback(final String path, final Pointer buffer, final long size, final long offset, final Pointer fi)
{
System.out.println("read was called");
return 0;
}
};
public Callback write = null;
public Callback statfs = null;
public Callback flush = null;
public Callback release = null;
public Callback fsync = null;
public Callback setxattr = null;
public Callback getxattr = null;
public Callback listxattr = null;
public Callback removexattr = null;
public Callback opendir = null;
public Callback readdir = new Callback()
{
public int callback(final String path, final Pointer buffer, final Pointer filler, final long offset,
final Pointer fi)
{
System.out.println("readdir was called");
return 0;
}
};
public Callback releasedir = null;
public Callback fsyncdir = null;
public Callback init = null;
public Callback destroy = null;
public Callback access = null;
public Callback create = null;
public Callback ftruncate = null;
public Callback fgetattr = null;
public Callback lock = null;
public Callback utimens = null;
public Callback bmap = null;
public int flag_nullpath_ok;
public int flag_reserved;
public Callback ioctl = null;
public Callback poll = null;
}
public static void main(final String[] args)
{
final String[] actualArgs = { "-f", "/some/mount/point" };
final Fuse fuse = (Fuse) Native.loadLibrary("fuse", Fuse.class);
final StructFuseOperations.ByReference operations = new StructFuseOperations.ByReference();
System.out.println("Mounting");
final int result = fuse.fuse_main_real(actualArgs.length, actualArgs, operations, operations.size(), null);
System.out.println("Result: " + result);
System.out.println("Mounted");
}
}
The definition of the the fuse_operations struct can be found here.
这可以编译使用:javac -cp path/to/jna.jar FuseTemp.java
并使用java -cp path/to/jna.jar:. FuseTemp调用
出现的错误是:fusermount: failed to access mountpoint /some/mount/point: Permission denied。
我在同一挂载点文件夹上以具有相同权限的同一用户身份执行这两个程序,并且我在 fuse 组中。我正在使用:
- Linux 内核 3.0.0
- 保险丝 2.8.4
- OpenJDK 1.6.0_23
- JNA 3.4.0
所以我的问题是:这两个程序(hello.c 和 FuseTemp.java)之间到底有什么不同,以及如何让它们做同样的事情?
提前致谢。
编辑:这是一些附加信息。
挂载点的初始stat:
File: `/some/mount/point'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 803h/2051d Inode: 540652 Links: 2
Access: (0777/drwxrwxrwx) Uid: ( 1000/ myusername) Gid: ( 1000/ myusername)
我作为普通用户运行 Java 程序得到的输出:
Mounting
fusermount: failed to access mountpoint /some/mount/point: Permission denied
Result: 1
Mounted
(program exits with return code 0)
在此之后,尝试执行stat 会出现以下错误消息:
stat: cannot stat/some/mount/point':传输端点未连接`
那是因为 Java 程序不再运行,所以 fuse 无法调用它的回调。要卸载,如果我尝试fusermount -u /some/mount/point,我会得到:
fusermount: entry for /some/mountpoint not found in /etc/mtab
如果我尝试sudo fusermount -u /some/mount/point,则挂载点已成功卸载,fusermount 没有输出。 /etc/mtab 是 chmod'd 644 (-rw-r--r--),所以我的用户可以阅读它,但它不包含 /some/mount/point。成功卸载后,挂载点恢复到原来的权限(777 目录)。
现在,以 root 身份运行 java 程序:
Mounting
Result: 1
Mounted
(program exits with return code 0)
之后stating/some/mount/point表示没有被修改过,即仍然是777目录。
我还重写了FuseTemp.java,将所有Callbacks 包含为Callbacks 而不是Pointers。但是,行为是相同的。
我查看了 fuse 的源代码,错误代码 1 可以在整个执行过程中的多个点返回。我会查明保险丝端到底是哪里出了故障,然后在这里报告。
现在对于hello.c:以普通用户身份运行它,从/some/mount/point 上的相同权限开始,并将参数-f 和/some/mount/point 传递给它,程序一开始不会打印任何输出,但会继续运行.在挂载点上运行stat 时,程序会打印
getattr was called
应该如此。 stat 返回一个错误,但这仅仅是因为hello.c 的getattr 函数没有给它任何信息,所以那里没有问题。以普通用户执行fusermount -u /some/mount/point后,程序退出,返回码0,卸载成功。
以 root 身份运行它,从 /some/mount/point 上的相同权限开始,并将参数 -f 和 /some/mount/point 传递给它,程序一开始不会打印任何输出,但会继续运行。在挂载点上运行 stat 时,我收到权限错误,因为我不是 root。当以 root 身份在其上运行 stat 时,程序会打印
getattr was called
应该如此。以普通用户身份执行 fusermount -u /some/mount/point 会产生
fusermount: entry for /some/mount/point not found in /etc/mtab
以root身份执行fusermount,程序退出,返回码0,卸载成功。
【问题讨论】:
-
不知道,但有趣的是看到 jar 发出
permission denied错误。如果在java命令之前使用linux命令su,是否有效? -
您是否将系统设置为允许使用 linux
chmod命令执行您的 jar 文件? -
在 jna.jar 上使用
chmod +x什么也没做(为什么会这样?Java 只需要读取它)。但是,以 root 身份运行程序不会导致出现错误。唉,它似乎什么也没做:它只是立即说“Mounting”和“Mounted”。所以它确实会完成,但fuse_main_real调用会立即返回。它返回的数字是1。这是一些进步,但程序需要像 hello.c 这样的普通用户可以运行。 -
其实我问这样的问题是为了证实我的怀疑。当涉及到文件系统调用时,Java 应用程序似乎正在使用其默认的安全权限模型,在 Linux 下,只有 root/系统用户帐户可以与文件系统调用交互以修改文件系统。非 root/系统用户将获得权限被拒绝或出现相同的对话框。在 Windows Vista/7 中,如果您不在管理员组中,它将触发 UAC。
-
您的 Java JNA 程序似乎仅在超级用户模式下运行时才有效,尽管返回错误为 1。因此,您有机会改进/纠正您的 JNA 代码以解决错误超级用户模式。
标签: java c arguments mount fuse