1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.mtp;
18
19import android.os.ParcelFileDescriptor;
20import android.os.storage.StorageManager;
21import android.system.ErrnoException;
22import android.system.Os;
23import android.test.AndroidTestCase;
24import android.test.suitebuilder.annotation.MediumTest;
25
26import libcore.io.IoUtils;
27
28import java.io.File;
29import java.io.FileNotFoundException;
30import java.io.IOException;
31import java.util.Arrays;
32
33@MediumTest
34public class AppFuseTest extends AndroidTestCase {
35    public void testMount() throws ErrnoException, IOException {
36        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
37        final AppFuse appFuse = new AppFuse("test", new TestCallback());
38        appFuse.mount(storageManager);
39        final File file = appFuse.getMountPoint();
40        assertTrue(file.isDirectory());
41        assertEquals(1, Os.stat(file.getPath()).st_ino);
42        appFuse.close();
43        assertTrue(1 != Os.stat(file.getPath()).st_ino);
44    }
45
46    public void testOpenFile() throws IOException {
47        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
48        final int INODE = 10;
49        final AppFuse appFuse = new AppFuse(
50                "test",
51                new TestCallback() {
52                    @Override
53                    public long getFileSize(int inode) throws FileNotFoundException {
54                        if (INODE == inode) {
55                            return 1024;
56                        }
57                        throw new FileNotFoundException();
58                    }
59                });
60        appFuse.mount(storageManager);
61        final ParcelFileDescriptor fd = appFuse.openFile(
62                INODE, ParcelFileDescriptor.MODE_READ_ONLY);
63        fd.close();
64        appFuse.close();
65    }
66
67    public void testOpenFile_fileNotFound() throws IOException {
68        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
69        final int INODE = 10;
70        final AppFuse appFuse = new AppFuse("test", new TestCallback());
71        appFuse.mount(storageManager);
72        try {
73            appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_ONLY);
74            fail();
75        } catch (FileNotFoundException exp) {}
76        appFuse.close();
77    }
78
79    public void testOpenFile_illegalMode() throws IOException {
80        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
81        final int INODE = 10;
82        final AppFuse appFuse = new AppFuse("test", new TestCallback());
83        appFuse.mount(storageManager);
84        try {
85            appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_WRITE);
86            fail();
87        } catch (IllegalArgumentException exp) {}
88        appFuse.close();
89    }
90
91    public void testReadFile() throws IOException {
92        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
93        final int fileInode = 10;
94        final byte[] fileBytes = new byte[] { 'a', 'b', 'c', 'd', 'e' };
95        final AppFuse appFuse = new AppFuse(
96                "test",
97                new TestCallback() {
98                    @Override
99                    public long getFileSize(int inode) throws FileNotFoundException {
100                        if (inode == fileInode) {
101                            return fileBytes.length;
102                        }
103                        return super.getFileSize(inode);
104                    }
105
106                    @Override
107                    public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
108                            throws IOException {
109                        if (inode == fileInode) {
110                            int i = 0;
111                            while (i < size && i + offset < fileBytes.length)  {
112                                bytes[i] = fileBytes[(int) (i + offset)];
113                                i++;
114                            }
115                            return i;
116                        }
117                        return super.readObjectBytes(inode, offset, size, bytes);
118                    }
119                });
120        appFuse.mount(storageManager);
121        final ParcelFileDescriptor fd = appFuse.openFile(
122                fileInode, ParcelFileDescriptor.MODE_READ_ONLY);
123        try (final ParcelFileDescriptor.AutoCloseInputStream stream =
124                new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
125            final byte[] buffer = new byte[1024];
126            final int size = stream.read(buffer, 0, buffer.length);
127            assertEquals(5, size);
128        }
129        appFuse.close();
130    }
131
132    public void testWriteFile() throws IOException {
133        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
134        final int INODE = 10;
135        final byte[] resultBytes = new byte[5];
136        final AppFuse appFuse = new AppFuse(
137                "test",
138                new TestCallback() {
139                    @Override
140                    public long getFileSize(int inode) throws FileNotFoundException {
141                        if (inode != INODE) {
142                            throw new FileNotFoundException();
143                        }
144                        return resultBytes.length;
145                    }
146
147                    @Override
148                    public int writeObjectBytes(
149                            long fileHandle, int inode, long offset, int size, byte[] bytes) {
150                        for (int i = 0; i < size; i++) {
151                            resultBytes[(int)(offset + i)] = bytes[i];
152                        }
153                        return size;
154                    }
155                });
156        appFuse.mount(storageManager);
157        final ParcelFileDescriptor fd = appFuse.openFile(
158                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
159        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
160                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
161            stream.write('a');
162            stream.write('b');
163            stream.write('c');
164            stream.write('d');
165            stream.write('e');
166        }
167        final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
168        assertTrue(Arrays.equals(BYTES, resultBytes));
169        appFuse.close();
170    }
171
172    public void testWriteFile_writeError() throws IOException {
173        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
174        final int INODE = 10;
175        final AppFuse appFuse = new AppFuse(
176                "test",
177                new TestCallback() {
178                    @Override
179                    public long getFileSize(int inode) throws FileNotFoundException {
180                        if (inode != INODE) {
181                            throw new FileNotFoundException();
182                        }
183                        return 5;
184                    }
185                });
186        appFuse.mount(storageManager);
187        final ParcelFileDescriptor fd = appFuse.openFile(
188                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
189        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
190                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
191            stream.write('a');
192            fail();
193        } catch (IOException e) {
194        }
195        appFuse.close();
196    }
197
198    public void testWriteFile_flushError() throws IOException {
199        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
200        final int INODE = 10;
201        final AppFuse appFuse = new AppFuse(
202                "test",
203                new TestCallback() {
204                    @Override
205                    public long getFileSize(int inode) throws FileNotFoundException {
206                        if (inode != INODE) {
207                            throw new FileNotFoundException();
208                        }
209                        return 5;
210                    }
211
212                    @Override
213                    public int writeObjectBytes(
214                            long fileHandle, int inode, long offset, int size, byte[] bytes) {
215                        return size;
216                    }
217
218                    @Override
219                    public void flushFileHandle(long fileHandle) throws IOException {
220                        throw new IOException();
221                    }
222                });
223        appFuse.mount(storageManager);
224        final ParcelFileDescriptor fd = appFuse.openFile(
225                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE);
226        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
227                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
228            stream.write('a');
229            try {
230                IoUtils.close(fd.getFileDescriptor());
231                fail();
232            } catch (IOException e) {
233            }
234        }
235        appFuse.close();
236    }
237
238    private static class TestCallback implements AppFuse.Callback {
239        @Override
240        public long getFileSize(int inode) throws FileNotFoundException {
241            throw new FileNotFoundException();
242        }
243
244        @Override
245        public long readObjectBytes(int inode, long offset, long size, byte[] bytes)
246                throws IOException {
247            throw new IOException();
248        }
249
250        @Override
251        public int writeObjectBytes(long fileHandle, int inode, long offset, int size, byte[] bytes)
252                throws IOException {
253            throw new IOException();
254        }
255
256        @Override
257        public void flushFileHandle(long fileHandle) throws IOException {}
258
259        @Override
260        public void closeFileHandle(long fileHandle) {}
261    }
262}
263