2

学習目的で、あらゆる種類のネットワーク リソース (HTTP/FTP ディレクトリなど) をローカル フォルダーとしてマウントできるものを開発しようとしています。

このライブラリで FUSE + JAVA を使用しています: https://github.com/EtiennePerot/fuse-jna

私が Java を選択したのは、これを Web サービスとして機能させる必要があり、JAVA でしかこれを行う方法を知らないからです。

さて、この短いイントロの後、私のシナリオは次のとおりです。

 _______________                ____________             __________
|               |              |            |           |          |
|remote resource|<--HTTP/FTP-->|fuse machine|<--SAMBA-->|desktop pc|
|_______________|              |____________|           |__________| 

私はこの例から始めました:

package net.fusejna.examples;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import net.fusejna.DirectoryFiller;
import net.fusejna.ErrorCodes;
import net.fusejna.StructFuseFileInfo.FileInfoWrapper;
import net.fusejna.StructStat.StatWrapper;
import net.fusejna.types.TypeMode.ModeWrapper;
import net.fusejna.types.TypeMode.NodeType;
import net.fusejna.util.FuseFilesystemAdapterAssumeImplemented;

public final class MemoryFS extends FuseFilesystemAdapterAssumeImplemented
{
    private final class MemoryDirectory extends MemoryPath
    {
        private final List<MemoryPath> contents = new ArrayList<MemoryPath>();

        private MemoryDirectory(final String name, final MemoryDirectory parent)
        {
            super(name, parent);
        }

        public void add(final MemoryPath p)
        {
            contents.add(p);
            p.parent = this;
        }

        @Override
        protected MemoryPath find(String path)
        {
            if (super.find(path) != null) {
                return super.find(path);
            }
            while (path.startsWith("/")) {
                path = path.substring(1);
            }
            if (!path.contains("/")) {
                for (final MemoryPath p : contents) {
                    if (p.name.equals(path)) {
                        return p;
                    }
                }
                return null;
            }
            final String nextName = path.substring(0, path.indexOf("/"));
            final String rest = path.substring(path.indexOf("/"));
            for (final MemoryPath p : contents) {
                if (p.name.equals(nextName)) {
                    return p.find(rest);
                }
            }
            return null;
        }

        @Override
        protected void getattr(final StatWrapper stat)
        {
            stat.setMode(NodeType.DIRECTORY);
        }

        private void mkdir(final String lastComponent)
        {
            contents.add(new MemoryDirectory(lastComponent, this));
        }

        public void mkfile(final String lastComponent)
        {
            contents.add(new MemoryFile(lastComponent, this));
        }

        public void read(final DirectoryFiller filler)
        {
            for (final MemoryPath p : contents) {
                filler.add(p.name);
            }
        }
    }

    private final class MemoryFile extends MemoryPath
    {
        private ByteBuffer contents = ByteBuffer.allocate(0);

        private MemoryFile(final String name, final MemoryDirectory parent)
        {
            super(name, parent);
        }

        @Override
        protected void getattr(final StatWrapper stat)
        {
            stat.setMode(NodeType.FILE);
            stat.size(contents.capacity());
        }

        private int read(final ByteBuffer buffer, final long size, final long offset)
        {
            final int bytesToRead = (int) Math.min(contents.capacity() - offset, size);
            final byte[] bytesRead = new byte[bytesToRead];
            contents.get(bytesRead, (int) offset, bytesToRead);
            buffer.put(bytesRead);
            contents.position(0); // Rewind
            return bytesToRead;
        }

        private void truncate(final long size)
        {
            if (size < contents.capacity()) {
                // Need to create a new, smaller buffer
                final ByteBuffer newContents = ByteBuffer.allocate((int) size);
                final byte[] bytesRead = new byte[(int) size];
                contents.get(bytesRead);
                newContents.put(bytesRead);
                contents = newContents;
            }
        }

        private int write(final ByteBuffer buffer, final long bufSize, final long writeOffset)
        {
            final int maxWriteIndex = (int) (writeOffset + bufSize);
            if (maxWriteIndex > contents.capacity()) {
                // Need to create a new, larger buffer
                final ByteBuffer newContents = ByteBuffer.allocate(maxWriteIndex);
                newContents.put(contents);
                contents = newContents;
            }
            final byte[] bytesToWrite = new byte[(int) bufSize];
            buffer.get(bytesToWrite, 0, (int) bufSize);
            contents.position((int) writeOffset);
            contents.put(bytesToWrite);
            contents.position(0); // Rewind
            return (int) bufSize;
        }
    }

    private abstract class MemoryPath
    {
        private final String name;
        private MemoryDirectory parent;

        private MemoryPath(final String name, final MemoryDirectory parent)
        {
            this.name = name;
            this.parent = parent;
        }

        private void delete()
        {
            if (parent != null) {
                parent.contents.remove(this);
                parent = null;
            }
        }

        protected MemoryPath find(String path)
        {
            while (path.startsWith("/")) {
                path = path.substring(1);
            }
            if (path.equals(name) || path.isEmpty()) {
                return this;
            }
            return null;
        }

        protected abstract void getattr(StatWrapper stat);
    }

    public static void main(final String... args)
    {
        if (args.length != 1) {
            System.err.println("Usage: MemoryFS <mountpoint>");
            System.exit(1);
        }
        try {
            new MemoryFS().log(true).mount(args[0]);
        }
        catch (final Throwable e) {
            System.err.println(e);
        }
    }

    private final MemoryDirectory rootDirectory = new MemoryDirectory("", null);

    public MemoryFS()
    {
        // Nothing
    }

    @Override
    public int access(final String path, final int access)
    {
        return 0;
    }

    @Override
    public int create(final String path, final ModeWrapper mode, final FileInfoWrapper info)
    {
        if (getPath(path) != null) {
            return -ErrorCodes.EEXIST;
        }
        final MemoryPath parent = getParentPath(path);
        if (parent instanceof MemoryDirectory) {
            ((MemoryDirectory) parent).mkfile(getLastComponent(path));
            return 0;
        }
        return -ErrorCodes.ENOENT;
    }

    @Override
    public int getattr(final String path, final StatWrapper stat)
    {
        final MemoryPath p = getPath(path);
        if (p != null) {
            p.getattr(stat);
            return 0;
        }
        return -ErrorCodes.ENOENT;
    }

    private String getLastComponent(String path)
    {
        while (path.substring(path.length() - 1).equals("/")) {
            path = path.substring(0, path.length() - 1);
        }
        if (path.isEmpty()) {
            return "";
        }
        return path.substring(path.lastIndexOf("/") + 1);
    }

    private MemoryPath getParentPath(final String path)
    {
        return rootDirectory.find(path.substring(0, path.lastIndexOf("/")));
    }

    private MemoryPath getPath(final String path)
    {
        return rootDirectory.find(path);
    }

    @Override
    public int mkdir(final String path, final ModeWrapper mode)
    {
        if (getPath(path) != null) {
            return -ErrorCodes.EEXIST;
        }
        final MemoryPath parent = getParentPath(path);
        if (parent instanceof MemoryDirectory) {
            ((MemoryDirectory) parent).mkdir(getLastComponent(path));
            return 0;
        }
        return -ErrorCodes.ENOENT;
    }

    @Override
    public int open(final String path, final FileInfoWrapper info)
    {
        return 0;
    }

    @Override
    public int read(final String path, final ByteBuffer buffer, final long size, final long offset, final FileInfoWrapper info)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        if (!(p instanceof MemoryFile)) {
            return -ErrorCodes.EISDIR;
        }
        return ((MemoryFile) p).read(buffer, size, offset);
    }

    @Override
    public int readdir(final String path, final DirectoryFiller filler)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        if (!(p instanceof MemoryDirectory)) {
            return -ErrorCodes.ENOTDIR;
        }
        ((MemoryDirectory) p).read(filler);
        return 0;
    }

    @Override
    public int rename(final String path, final String newName)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        final MemoryPath newParent = getParentPath(newName);
        if (newParent != null) {
            return -ErrorCodes.ENOENT;
        }
        if (!(newParent instanceof MemoryDirectory)) {
            return -ErrorCodes.ENOTDIR;
        }
        p.delete();
        ((MemoryDirectory) newParent).add(p);
        return 0;
    }

    @Override
    public int rmdir(final String path)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        if (!(p instanceof MemoryDirectory)) {
            return -ErrorCodes.ENOTDIR;
        }
        p.delete();
        return 0;
    }

    @Override
    public int truncate(final String path, final long offset)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        if (!(p instanceof MemoryFile)) {
            return -ErrorCodes.EISDIR;
        }
        ((MemoryFile) p).truncate(offset);
        return 0;
    }

    @Override
    public int unlink(final String path)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        p.delete();
        return 0;
    }

    @Override
    public int write(final String path, final ByteBuffer buf, final long bufSize, final long writeOffset,
            final FileInfoWrapper wrapper)
    {
        final MemoryPath p = getPath(path);
        if (p == null) {
            return -ErrorCodes.ENOENT;
        }
        if (!(p instanceof MemoryFile)) {
            return -ErrorCodes.EISDIR;
        }
        return ((MemoryFile) p).write(buf, bufSize, writeOffset);
    }
}

しかし、ここで問題が発生します。

私が間違っていなければ、FUSE はディレクトリを一覧表示する際にファイルの内容をロードします。

これは、ユーザーがファイルを開いていない場合でも、ファイルを完全にロードする必要があることを意味します。

最初の質問は、プロキシ パターンを使用せずに「オンデマンド」でファイルをロードするにはどうすればよいかということです。(ファイルシステム全体を RAM にロードするのは誰ですか??)

最初の問題はリモート リソースとヒューズ マシンの間にありますが、2 番目の問題は一歩前進し、ヒューズ マシンとデスクトップ PC に関係しています。

リモート リソースについて話しているので、デスクトップ pc と共有する前に、Fuse マシンがそれらをダウンロードする必要があることを意味します。もちろん、これにより同じリソースの 2 つのダウンロードが発生します。1 つはヒューズによって開始され、もう 1 つはデスクトップ PC によって開始されます。

2 番目の質問は次のとおりです。Fuse マシンがファイルを開いたときに、デスクトップ PC へのリンクのようなものを返すようにする方法はありますか? (デスクトップPCがファイルを要求するように、ヒューズマシンはそれに「リンク」を提供し、2つのダウンロードの1つを回避して、リモートリソースから直接ダウンロードを開始します)。

テキストが少し混乱していることは知っていますが、テキスト自体よりもこの問題についてはるかに混乱しているため、必要なことをより適切に説明できなかったら申し訳ありませんが、どうすればよいかわかりませんそれを尋ねる!

4

0 に答える 0