blob: 91dda0899a63367b177ea58f3639d32165d7abba [file] [log] [blame]
Songchun Fand1b41d42019-11-12 17:09:53 -08001/*
2 * Copyright (C) 2019 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 android.os.incremental;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.os.RemoteException;
22
23import java.io.File;
24import java.io.IOException;
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080025import java.nio.ByteBuffer;
26import java.util.UUID;
Songchun Fand1b41d42019-11-12 17:09:53 -080027
28/**
Songchun Fanf5c894f2019-11-29 15:43:58 -080029 * Provides operations on an Incremental File System directory, using IncrementalServiceNative.
30 * Example usage:
Songchun Fand1b41d42019-11-12 17:09:53 -080031 *
32 * <blockquote><pre>
Songchun Fanf5c894f2019-11-29 15:43:58 -080033 * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
Songchun Fand1b41d42019-11-12 17:09:53 -080034 * IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
35 * storage.makeDirectory("subdir");
36 * </pre></blockquote>
37 *
38 * @hide
39 */
40public final class IncrementalStorage {
41 private static final String TAG = "IncrementalStorage";
42 private final int mId;
Songchun Fanf5c894f2019-11-29 15:43:58 -080043 private final IIncrementalManagerNative mService;
Songchun Fand1b41d42019-11-12 17:09:53 -080044
45
Songchun Fanf5c894f2019-11-29 15:43:58 -080046 public IncrementalStorage(@NonNull IIncrementalManagerNative is, int id) {
Songchun Fand1b41d42019-11-12 17:09:53 -080047 mService = is;
48 mId = id;
49 }
50
51 public int getId() {
52 return mId;
53 }
54
55 /**
56 * Temporarily bind-mounts the current storage directory to a target directory. The bind-mount
57 * will NOT be preserved between device reboots.
58 *
59 * @param targetPath Absolute path to the target directory.
60 */
61 public void bind(@NonNull String targetPath) throws IOException {
62 bind("", targetPath);
63 }
64
65 /**
66 * Temporarily bind-mounts a subdir under the current storage directory to a target directory.
67 * The bind-mount will NOT be preserved between device reboots.
68 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080069 * @param sourcePath Source path as a relative path under current storage
70 * directory.
71 * @param targetPath Absolute path to the target directory.
Songchun Fand1b41d42019-11-12 17:09:53 -080072 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080073 public void bind(@NonNull String sourcePath, @NonNull String targetPath)
Songchun Fand1b41d42019-11-12 17:09:53 -080074 throws IOException {
75 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -080076 int res = mService.makeBindMount(mId, sourcePath, targetPath,
Songchun Fanf5c894f2019-11-29 15:43:58 -080077 IIncrementalManagerNative.BIND_TEMPORARY);
Songchun Fand1b41d42019-11-12 17:09:53 -080078 if (res < 0) {
79 throw new IOException("bind() failed with errno " + -res);
80 }
81 } catch (RemoteException e) {
82 e.rethrowFromSystemServer();
83 }
84 }
85
86
87 /**
88 * Permanently bind-mounts the current storage directory to a target directory. The bind-mount
89 * WILL be preserved between device reboots.
90 *
91 * @param targetPath Absolute path to the target directory.
92 */
93 public void bindPermanent(@NonNull String targetPath) throws IOException {
94 bindPermanent("", targetPath);
95 }
96
97 /**
98 * Permanently bind-mounts a subdir under the current storage directory to a target directory.
99 * The bind-mount WILL be preserved between device reboots.
100 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800101 * @param sourcePath Relative path under the current storage directory.
102 * @param targetPath Absolute path to the target directory.
Songchun Fand1b41d42019-11-12 17:09:53 -0800103 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800104 public void bindPermanent(@NonNull String sourcePath, @NonNull String targetPath)
Songchun Fand1b41d42019-11-12 17:09:53 -0800105 throws IOException {
106 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800107 int res = mService.makeBindMount(mId, sourcePath, targetPath,
Songchun Fanf5c894f2019-11-29 15:43:58 -0800108 IIncrementalManagerNative.BIND_PERMANENT);
Songchun Fand1b41d42019-11-12 17:09:53 -0800109 if (res < 0) {
110 throw new IOException("bind() permanent failed with errno " + -res);
111 }
112 } catch (RemoteException e) {
113 e.rethrowFromSystemServer();
114 }
115 }
116
117 /**
118 * Unbinds a bind mount.
119 *
120 * @param targetPath Absolute path to the target directory.
121 */
122 public void unBind(@NonNull String targetPath) throws IOException {
123 try {
124 int res = mService.deleteBindMount(mId, targetPath);
125 if (res < 0) {
126 throw new IOException("unbind() failed with errno " + -res);
127 }
128 } catch (RemoteException e) {
129 e.rethrowFromSystemServer();
130 }
131 }
132
133 /**
134 * Creates a sub-directory under the current storage directory.
135 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800136 * @param path Relative path of the sub-directory, e.g., "subdir"
Songchun Fand1b41d42019-11-12 17:09:53 -0800137 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800138 public void makeDirectory(@NonNull String path) throws IOException {
Songchun Fand1b41d42019-11-12 17:09:53 -0800139 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800140 int res = mService.makeDirectory(mId, path);
Songchun Fand1b41d42019-11-12 17:09:53 -0800141 if (res < 0) {
142 throw new IOException("makeDirectory() failed with errno " + -res);
143 }
144 } catch (RemoteException e) {
145 e.rethrowFromSystemServer();
146 }
147 }
148
149 /**
150 * Creates a sub-directory under the current storage directory. If its parent dirs do not exist,
151 * create the parent dirs as well.
152 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800153 * @param path Full path.
Songchun Fand1b41d42019-11-12 17:09:53 -0800154 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800155 public void makeDirectories(@NonNull String path) throws IOException {
Songchun Fand1b41d42019-11-12 17:09:53 -0800156 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800157 int res = mService.makeDirectories(mId, path);
Songchun Fand1b41d42019-11-12 17:09:53 -0800158 if (res < 0) {
159 throw new IOException("makeDirectory() failed with errno " + -res);
160 }
161 } catch (RemoteException e) {
162 e.rethrowFromSystemServer();
163 }
164 }
165
166 /**
167 * Creates a file under the current storage directory.
168 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800169 * @param path Relative path of the new file.
Songchun Fand1b41d42019-11-12 17:09:53 -0800170 * @param size Size of the new file in bytes.
171 * @param metadata Metadata bytes.
172 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800173 public void makeFile(@NonNull String path, long size, @Nullable UUID id,
174 @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash,
175 @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException {
Songchun Fand1b41d42019-11-12 17:09:53 -0800176 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800177 final IncrementalNewFileParams params = new IncrementalNewFileParams();
178 params.size = size;
179 params.metadata = metadata;
180 params.fileId = idToBytes(id);
181 if (hashAlgorithm != 0 || signature != null) {
182 params.signature = new IncrementalSignature();
183 params.signature.hashAlgorithm = hashAlgorithm;
184 params.signature.rootHash = rootHash;
185 params.signature.additionalData = additionalData;
186 params.signature.signature = signature;
187 }
188 int res = mService.makeFile(mId, path, params);
189 if (res != 0) {
Songchun Fand1b41d42019-11-12 17:09:53 -0800190 throw new IOException("makeFile() failed with errno " + -res);
191 }
192 } catch (RemoteException e) {
193 e.rethrowFromSystemServer();
194 }
195 }
196
197 /**
198 * Creates a file in Incremental storage. The content of the file is mapped from a range inside
199 * a source file in the same storage.
200 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800201 * @param destPath Target full path.
202 * @param sourcePath Source full path.
Songchun Fand1b41d42019-11-12 17:09:53 -0800203 * @param rangeStart Starting offset (in bytes) in the source file.
204 * @param rangeEnd Ending offset (in bytes) in the source file.
205 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800206 public void makeFileFromRange(@NonNull String destPath,
207 @NonNull String sourcePath, long rangeStart, long rangeEnd) throws IOException {
Songchun Fand1b41d42019-11-12 17:09:53 -0800208 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800209 int res = mService.makeFileFromRange(mId, destPath, sourcePath,
Songchun Fand1b41d42019-11-12 17:09:53 -0800210 rangeStart, rangeEnd);
211 if (res < 0) {
212 throw new IOException("makeFileFromRange() failed, errno " + -res);
213 }
214 } catch (RemoteException e) {
215 e.rethrowFromSystemServer();
216 }
217 }
218
219 /**
220 * Creates a hard-link between two paths, which can be under different storages but in the same
221 * Incremental File System.
222 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800223 * @param sourcePath The absolute path of the source.
224 * @param destStorage The target storage of the link target.
225 * @param destPath The absolute path of the target.
Songchun Fand1b41d42019-11-12 17:09:53 -0800226 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800227 public void makeLink(@NonNull String sourcePath, IncrementalStorage destStorage,
228 @NonNull String destPath) throws IOException {
Songchun Fand1b41d42019-11-12 17:09:53 -0800229 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800230 int res = mService.makeLink(mId, sourcePath, destStorage.getId(),
231 destPath);
Songchun Fand1b41d42019-11-12 17:09:53 -0800232 if (res < 0) {
233 throw new IOException("makeLink() failed with errno " + -res);
234 }
235 } catch (RemoteException e) {
236 e.rethrowFromSystemServer();
237 }
238 }
239
240 /**
241 * Deletes a hard-link under the current storage directory.
242 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800243 * @param path The absolute path of the target.
Songchun Fand1b41d42019-11-12 17:09:53 -0800244 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800245 public void unlink(@NonNull String path) throws IOException {
Songchun Fand1b41d42019-11-12 17:09:53 -0800246 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800247 int res = mService.unlink(mId, path);
Songchun Fand1b41d42019-11-12 17:09:53 -0800248 if (res < 0) {
249 throw new IOException("unlink() failed with errno " + -res);
250 }
251 } catch (RemoteException e) {
252 e.rethrowFromSystemServer();
253 }
254 }
255
256 /**
257 * Rename an old file name to a new file name under the current storage directory.
258 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800259 * @param sourcepath Old file path as a full path to the storage directory.
260 * @param destpath New file path as a full path to the storage directory.
Songchun Fand1b41d42019-11-12 17:09:53 -0800261 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800262 public void moveFile(@NonNull String sourcepath,
263 @NonNull String destpath) throws IOException {
264 //TODO(zyy): implement using rename(2) when confirmed that IncFS supports it.
Songchun Fand1b41d42019-11-12 17:09:53 -0800265 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800266 int res = mService.makeLink(mId, sourcepath, mId, destpath);
Songchun Fand1b41d42019-11-12 17:09:53 -0800267 if (res < 0) {
268 throw new IOException("moveFile() failed at makeLink(), errno " + -res);
269 }
270 } catch (RemoteException e) {
271 e.rethrowFromSystemServer();
272 }
273 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800274 mService.unlink(mId, sourcepath);
Songchun Fand1b41d42019-11-12 17:09:53 -0800275 } catch (RemoteException ignored) {
276 }
277 }
278
279 /**
280 * Move a directory, which is bind-mounted to a given storage, to a new location. The bind mount
281 * will be persistent between reboots.
282 *
283 * @param sourcePath The old path of the directory as an absolute path.
284 * @param destPath The new path of the directory as an absolute path, expected to already
285 * exist.
286 */
287 public void moveDir(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
288 if (!new File(destPath).exists()) {
289 throw new IOException("moveDir() requires that destination dir already exists.");
290 }
291 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800292 int res = mService.makeBindMount(mId, sourcePath, destPath,
Songchun Fanf5c894f2019-11-29 15:43:58 -0800293 IIncrementalManagerNative.BIND_PERMANENT);
Songchun Fand1b41d42019-11-12 17:09:53 -0800294 if (res < 0) {
295 throw new IOException("moveDir() failed at making bind mount, errno " + -res);
296 }
297 } catch (RemoteException e) {
298 e.rethrowFromSystemServer();
299 }
300 try {
301 mService.deleteBindMount(mId, sourcePath);
302 } catch (RemoteException ignored) {
303 }
304 }
305
306 /**
307 * Checks whether a file under the current storage directory is fully loaded.
308 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800309 * @param path The relative path of the file.
Songchun Fand1b41d42019-11-12 17:09:53 -0800310 * @return True if the file is fully loaded.
311 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800312 public boolean isFileFullyLoaded(@NonNull String path) {
313 return isFileRangeLoaded(path, 0, -1);
Songchun Fand1b41d42019-11-12 17:09:53 -0800314 }
315
316 /**
317 * Checks whether a range in a file if loaded.
318 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800319 * @param path The relative path of the file.
Songchun Fand1b41d42019-11-12 17:09:53 -0800320 * @param start The starting offset of the range.
321 * @param end The ending offset of the range.
322 * @return True if the file is fully loaded.
323 */
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800324 public boolean isFileRangeLoaded(@NonNull String path, long start, long end) {
Songchun Fand1b41d42019-11-12 17:09:53 -0800325 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800326 return mService.isFileRangeLoaded(mId, path, start, end);
Songchun Fand1b41d42019-11-12 17:09:53 -0800327 } catch (RemoteException e) {
328 e.rethrowFromSystemServer();
329 return false;
330 }
331 }
332
333 /**
334 * Returns the metadata object of an IncFs File.
335 *
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800336 * @param path The relative path of the file.
Songchun Fand1b41d42019-11-12 17:09:53 -0800337 * @return Byte array that contains metadata bytes.
338 */
339 @Nullable
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800340 public byte[] getFileMetadata(@NonNull String path) {
Songchun Fand1b41d42019-11-12 17:09:53 -0800341 try {
Yurii Zubrytskyi4a25dfb2020-01-10 11:53:24 -0800342 return mService.getMetadataByPath(mId, path);
343 } catch (RemoteException e) {
344 e.rethrowFromSystemServer();
345 return null;
346 }
347 }
348
349 private static final int UUID_BYTE_SIZE = 16;
350
351 /**
352 * Converts UUID to a byte array usable for Incremental API calls
353 *
354 * @param id The id to convert
355 * @return Byte array that contains the same ID.
356 */
357 public static byte[] idToBytes(UUID id) {
358 if (id == null) {
359 return null;
360 }
361 final ByteBuffer buf = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
362 buf.putLong(id.getMostSignificantBits());
363 buf.putLong(id.getLeastSignificantBits());
364 return buf.array();
365 }
366
367 /**
368 * Converts UUID from a byte array usable for Incremental API calls
369 *
370 * @param bytes The id in byte array format, 16 bytes long
371 * @return UUID constructed from the byte array.
372 */
373 public static UUID bytesToId(byte[] bytes) {
374 if (bytes.length != UUID_BYTE_SIZE) {
375 throw new IllegalArgumentException("Expected array of size " + UUID_BYTE_SIZE
376 + ", got " + bytes.length);
377 }
378 final ByteBuffer buf = ByteBuffer.wrap(bytes);
379 long msb = buf.getLong();
380 long lsb = buf.getLong();
381 return new UUID(msb, lsb);
382 }
383
384 /**
385 * Returns the metadata object of an IncFs File.
386 *
387 * @param id The file id.
388 * @return Byte array that contains metadata bytes.
389 */
390 @Nullable
391 public byte[] getFileMetadata(@NonNull UUID id) {
392 try {
393 final byte[] rawId = idToBytes(id);
394 return mService.getMetadataById(mId, rawId);
Songchun Fand1b41d42019-11-12 17:09:53 -0800395 } catch (RemoteException e) {
396 e.rethrowFromSystemServer();
397 return null;
398 }
399 }
400
401 /**
402 * Informs the data loader service associated with the current storage to start data loader
403 *
404 * @return True if data loader is successfully started.
405 */
406 public boolean startLoading() {
407 try {
408 return mService.startLoading(mId);
409 } catch (RemoteException e) {
410 e.rethrowFromSystemServer();
411 return false;
412 }
413 }
414}