FuseAppLoop.java revision 9fb00183a04036a58ee208f5bfd6c9768982f0aa
1878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono/*
2878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * Copyright (C) 2016 The Android Open Source Project
3878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono *
4878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * Licensed under the Apache License, Version 2.0 (the "License");
5878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * you may not use this file except in compliance with the License.
6878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * You may obtain a copy of the License at
7878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono *
8878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono *      http://www.apache.org/licenses/LICENSE-2.0
9878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono *
10878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * Unless required by applicable law or agreed to in writing, software
11878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * distributed under the License is distributed on an "AS IS" BASIS,
12878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * See the License for the specific language governing permissions and
14878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono * limitations under the License.
15878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono */
16878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
17878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironopackage com.android.internal.os;
18878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
19878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.annotation.NonNull;
20878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.annotation.Nullable;
219fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hironoimport android.os.ProxyFileDescriptorCallback;
22878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.os.ParcelFileDescriptor;
23878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.system.ErrnoException;
24878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.system.OsConstants;
25878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.util.Log;
26878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport android.util.SparseArray;
27878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport com.android.internal.annotations.GuardedBy;
289fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hironoimport com.android.internal.annotations.VisibleForTesting;
29878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport com.android.internal.util.Preconditions;
30878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
31878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironoimport java.io.IOException;
329fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hironoimport java.util.concurrent.ThreadFactory;
33878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
34878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hironopublic class FuseAppLoop {
35878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private static final String TAG = "FuseAppLoop";
36878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
37878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    public static final int ROOT_INODE = 1;
38878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private static final int MIN_INODE = 2;
399fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    private static final ThreadFactory sDefaultThreadFactory = new ThreadFactory() {
409fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        @Override
419fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        public Thread newThread(Runnable r) {
429fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            return new Thread(r, TAG);
439fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        }
449fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    };
45878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
46878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private final Object mLock = new Object();
479fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    private final int mMountPointId;
489fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    private final Thread mThread;
49878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
50878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @GuardedBy("mLock")
51878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private final SparseArray<CallbackEntry> mCallbackMap = new SparseArray<>();
52878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
53878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    /**
54878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono     * Sequential number can be used as file name and inode in AppFuse.
55878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono     * 0 is regarded as an error, 1 is mount point. So we start the number from 2.
56878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono     */
57878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @GuardedBy("mLock")
58878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private int mNextInode = MIN_INODE;
59878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
609fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    private FuseAppLoop(
619fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            int mountPointId, @NonNull ParcelFileDescriptor fd, @Nullable ThreadFactory factory) {
629fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        mMountPointId = mountPointId;
63878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        final int rawFd = fd.detachFd();
649fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        if (factory == null) {
659fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            factory = sDefaultThreadFactory;
669fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        }
679fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        mThread = factory.newThread(new Runnable() {
68878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            @Override
69878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            public void run() {
709fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                // rawFd is closed by native_start_loop. Java code does not need to close it.
719fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                native_start_loop(rawFd);
72878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
739fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        });
749fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    }
759fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono
769fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    public static @NonNull FuseAppLoop open(int mountPointId, @NonNull ParcelFileDescriptor fd,
779fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            @Nullable ThreadFactory factory) {
789fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        Preconditions.checkNotNull(fd);
799fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        final FuseAppLoop loop = new FuseAppLoop(mountPointId, fd, factory);
809fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        loop.mThread.start();
819fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        return loop;
82878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
83878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
849fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    public int registerCallback(@NonNull ProxyFileDescriptorCallback callback)
85878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            throws UnmountedException, IOException {
869fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        if (mThread.getState() == Thread.State.TERMINATED) {
879fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            throw new UnmountedException();
889fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        }
89878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized (mLock) {
90878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            if (mCallbackMap.size() >= Integer.MAX_VALUE - MIN_INODE) {
91878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                throw new IOException("Too many opened files.");
92878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
939fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            int id;
94878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            while (true) {
95878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                id = mNextInode;
96878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                mNextInode++;
97878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                if (mNextInode < 0) {
98878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                    mNextInode = MIN_INODE;
99878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                }
100878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                if (mCallbackMap.get(id) == null) {
101878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                    break;
102878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                }
103878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
104878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            mCallbackMap.put(id, new CallbackEntry(callback));
1059fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            return id;
106878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
107878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
108878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
1099fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    public void unregisterCallback(int id) {
1109fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        mCallbackMap.remove(id);
1119fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    }
1129fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono
1139fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    public int getMountPointId() {
1149fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        return mMountPointId;
115878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
116878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
117878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
118878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
119878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        if (entry != null) {
120878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            return entry;
121878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        } else {
122878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            throw new ErrnoException("getCallbackEntry", OsConstants.ENOENT);
123878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
124878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
125878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
126878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    // Called by JNI.
127878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @SuppressWarnings("unused")
128878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private long onGetSize(long inode) {
129878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized(mLock) {
130878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            try {
131878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                return getCallbackEntryOrThrowLocked(inode).callback.onGetSize();
132878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            } catch (ErrnoException exp) {
1339fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return getError(exp);
134878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
135878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
136878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
137878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
138878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    // Called by JNI.
139878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @SuppressWarnings("unused")
140878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private int onOpen(long inode) {
141878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized(mLock) {
142878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            try {
143878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                final CallbackEntry entry = getCallbackEntryOrThrowLocked(inode);
144878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                if (entry.opened) {
145878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                    throw new ErrnoException("onOpen", OsConstants.EMFILE);
146878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                }
147878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                entry.opened = true;
148878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                // Use inode as file handle. It's OK because AppFuse does not allow to open the same
149878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                // file twice.
150878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                return (int) inode;
151878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            } catch (ErrnoException exp) {
1529fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return getError(exp);
153878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
154878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
155878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
156878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
157878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    // Called by JNI.
158878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @SuppressWarnings("unused")
159878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private int onFsync(long inode) {
160878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized(mLock) {
161878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            try {
162878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                getCallbackEntryOrThrowLocked(inode).callback.onFsync();
163878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                return 0;
164878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            } catch (ErrnoException exp) {
1659fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return getError(exp);
166878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
167878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
168878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
169878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
170878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    // Called by JNI.
171878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @SuppressWarnings("unused")
172878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private int onRelease(long inode) {
173878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized(mLock) {
1749fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            try {
1759fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                getCallbackEntryOrThrowLocked(inode).callback.onRelease();
1769fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return 0;
1779fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            } catch (ErrnoException exp) {
1789fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return getError(exp);
1799fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono            } finally {
1809fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                mCallbackMap.remove(checkInode(inode));
181878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
182878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
183878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
184878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
185878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    // Called by JNI.
186878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @SuppressWarnings("unused")
187878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private int onRead(long inode, long offset, int size, byte[] bytes) {
188878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized(mLock) {
189878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            try {
190878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                return getCallbackEntryOrThrowLocked(inode).callback.onRead(offset, size, bytes);
191878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            } catch (ErrnoException exp) {
1929fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return getError(exp);
193878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
194878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
195878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
196878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
197878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    // Called by JNI.
198878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    @SuppressWarnings("unused")
199878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private int onWrite(long inode, long offset, int size, byte[] bytes) {
200878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        synchronized(mLock) {
201878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            try {
202878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono                return getCallbackEntryOrThrowLocked(inode).callback.onWrite(offset, size, bytes);
203878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            } catch (ErrnoException exp) {
2049fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono                return getError(exp);
205878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            }
206878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
207878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
208878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
2099fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    private static int getError(@NonNull ErrnoException exp) {
2109fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        // Should not return ENOSYS because the kernel stops
2119fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        // dispatching the FUSE action once FUSE implementation returns ENOSYS for the action.
2129fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        return exp.errno != OsConstants.ENOSYS ? -exp.errno : -OsConstants.EIO;
2139fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    }
2149fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono
215878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    native boolean native_start_loop(int fd);
216878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
217878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private static int checkInode(long inode) {
218878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        Preconditions.checkArgumentInRange(inode, MIN_INODE, Integer.MAX_VALUE, "checkInode");
219878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        return (int) inode;
220878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
221878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
222878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    public static class UnmountedException extends Exception {}
223878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono
224878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    private static class CallbackEntry {
2259fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        final ProxyFileDescriptorCallback callback;
226878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        boolean opened;
2279fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        CallbackEntry(ProxyFileDescriptorCallback callback) {
228878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            Preconditions.checkNotNull(callback);
229878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono            this.callback = callback;
230878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono        }
231878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono    }
232878e86f38f87cc5dc537ffc623b04f9a779fb080Daichi Hirono}
233