Использование библиотеки FUSE с Java; попытка повторить пример hello.c

Я пытаюсь создать привязки к библиотеке FUSE, используя JNA, но на этом пути я столкнулся с проблемой. Я сократил код настолько, насколько это возможно, чтобы сделать его удобоваримым.

Библиотека FUSE поставляется с несколькими примерами файловых систем, написанных на C. Самый простой из них - hello.c. Ниже приведена минимизированная версия его кода, содержащая лишь несколько отпечатков в функциях файловой системы:

hello.c:

/*
  FUSE: Filesystem in Userspace
  Copyright (C) 2001-2007  Miklos Szeredi 

  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 
#include 
#include 
#include 
#include 

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() выполняется правильно. Я пытаюсь воспроизвести то же самое на Java, используя JNA. Вот что у меня получилось:

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");
    }
}

Определение структуры fuse_operations можно найти здесь.

Это можно скомпилировать, используя: javac -cp path/to/jna.jar FuseTemp.java

И вызвать с помощью java -cp path/to/jna.jar:. FuseTemp

jna.jar доступен здесь.

Возникает следующая ошибка: fusermount: failed to access mountpoint /some/mount/point: Permission denied.

Я выполняю обе программы от имени одного и того же пользователя с одинаковыми правами на ту же папку mountpoint, и я вхожу в группу fuse. Я использую:

  • Linux kernel 3.0.0
  • FUSE 2.8.4
  • OpenJDK 1.6.0_23
  • JNA 3.4.0

Итак, мой вопрос: Что именно отличается между этими двумя программами (hello.c и FuseTemp.java), и как заставить их делать одно и то же?

Спасибо заранее.

Edit: Вот некоторая дополнительная информация.

Начальная 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': Transport endpoint is not connected`

Это потому, что 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).

Теперь запускаю java-программу от имени root:

Mounting
Result: 1
Mounted
(program exits with return code 0)

После этого stating /some/mount/point показывает, что он не был изменен, т.е. это по-прежнему каталог 777.

Я также переписал FuseTemp.java, чтобы включить все Callbackы как Callbackы вместо Pointerов. Однако поведение остается таким же.

Я посмотрел исходный код fuse, и код ошибки 1 может быть возвращен в нескольких точках на протяжении всего выполнения. Я выясню, где именно происходит сбой на стороне fuse, и сообщу об этом здесь.

Теперь о 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. При выполнении stat на ней от имени root, программа печатает

getattr was called

как и должна. Выполнение fusermount -u /some/mount/point как обычный пользователь выходит

fusermount: entry for /some/mount/point not found in /etc/mtab

Выполняя fusermount как root, программа завершается с кодом возврата 0, и размонтирование проходит успешно.

11
задан Etienne Perot 18 January 2012 в 23:51
поделиться