1 /*
2  * Copyright (C) 2011 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 
17 package android.app.backup;
18 
19 import android.os.ParcelFileDescriptor;
20 import android.system.ErrnoException;
21 import android.system.Os;
22 import android.util.Log;
23 
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 
29 /**
30  * Global constant definitions et cetera related to the full-backup-to-fd
31  * binary format.  Nothing in this namespace is part of any API; it's all
32  * hidden details of the current implementation gathered into one location.
33  *
34  * @hide
35  */
36 public class FullBackup {
37     static final String TAG = "FullBackup";
38 
39     public static final String APK_TREE_TOKEN = "a";
40     public static final String OBB_TREE_TOKEN = "obb";
41     public static final String ROOT_TREE_TOKEN = "r";
42     public static final String DATA_TREE_TOKEN = "f";
43     public static final String NO_BACKUP_TREE_TOKEN = "nb";
44     public static final String DATABASE_TREE_TOKEN = "db";
45     public static final String SHAREDPREFS_TREE_TOKEN = "sp";
46     public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef";
47     public static final String CACHE_TREE_TOKEN = "c";
48     public static final String SHARED_STORAGE_TOKEN = "shared";
49 
50     public static final String APPS_PREFIX = "apps/";
51     public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
52 
53     public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
54     public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
55     public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken";
56 
57     /**
58      * @hide
59      */
backupToTar(String packageName, String domain, String linkdomain, String rootpath, String path, BackupDataOutput output)60     static public native int backupToTar(String packageName, String domain,
61             String linkdomain, String rootpath, String path, BackupDataOutput output);
62 
63     /**
64      * Copy data from a socket to the given File location on permanent storage.  The
65      * modification time and access mode of the resulting file will be set if desired,
66      * although group/all rwx modes will be stripped: the restored file will not be
67      * accessible from outside the target application even if the original file was.
68      * If the {@code type} parameter indicates that the result should be a directory,
69      * the socket parameter may be {@code null}; even if it is valid, no data will be
70      * read from it in this case.
71      * <p>
72      * If the {@code mode} argument is negative, then the resulting output file will not
73      * have its access mode or last modification time reset as part of this operation.
74      *
75      * @param data Socket supplying the data to be copied to the output file.  If the
76      *    output is a directory, this may be {@code null}.
77      * @param size Number of bytes of data to copy from the socket to the file.  At least
78      *    this much data must be available through the {@code data} parameter.
79      * @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data
80      *    or {@link BackupAgent#TYPE_DIRECTORY} for a directory.
81      * @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on
82      *    the output file or directory.  group/all rwx modes are stripped even if set
83      *    in this parameter.  If this parameter is negative then neither
84      *    the mode nor the mtime values will be applied to the restored file.
85      * @param mtime A timestamp in the standard Unix epoch that will be imposed as the
86      *    last modification time of the output file.  if the {@code mode} parameter is
87      *    negative then this parameter will be ignored.
88      * @param outFile Location within the filesystem to place the data.  This must point
89      *    to a location that is writeable by the caller, preferably using an absolute path.
90      * @throws IOException
91      */
restoreFile(ParcelFileDescriptor data, long size, int type, long mode, long mtime, File outFile)92     static public void restoreFile(ParcelFileDescriptor data,
93             long size, int type, long mode, long mtime, File outFile) throws IOException {
94         if (type == BackupAgent.TYPE_DIRECTORY) {
95             // Canonically a directory has no associated content, so we don't need to read
96             // anything from the pipe in this case.  Just create the directory here and
97             // drop down to the final metadata adjustment.
98             if (outFile != null) outFile.mkdirs();
99         } else {
100             FileOutputStream out = null;
101 
102             // Pull the data from the pipe, copying it to the output file, until we're done
103             try {
104                 if (outFile != null) {
105                     File parent = outFile.getParentFile();
106                     if (!parent.exists()) {
107                         // in practice this will only be for the default semantic directories,
108                         // and using the default mode for those is appropriate.
109                         parent.mkdirs();
110                     }
111                     out = new FileOutputStream(outFile);
112                 }
113             } catch (IOException e) {
114                 Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e);
115             }
116 
117             byte[] buffer = new byte[32 * 1024];
118             final long origSize = size;
119             FileInputStream in = new FileInputStream(data.getFileDescriptor());
120             while (size > 0) {
121                 int toRead = (size > buffer.length) ? buffer.length : (int)size;
122                 int got = in.read(buffer, 0, toRead);
123                 if (got <= 0) {
124                     Log.w(TAG, "Incomplete read: expected " + size + " but got "
125                             + (origSize - size));
126                     break;
127                 }
128                 if (out != null) {
129                     try {
130                         out.write(buffer, 0, got);
131                     } catch (IOException e) {
132                         // Problem writing to the file.  Quit copying data and delete
133                         // the file, but of course keep consuming the input stream.
134                         Log.e(TAG, "Unable to write to file " + outFile.getPath(), e);
135                         out.close();
136                         out = null;
137                         outFile.delete();
138                     }
139                 }
140                 size -= got;
141             }
142             if (out != null) out.close();
143         }
144 
145         // Now twiddle the state to match the backup, assuming all went well
146         if (mode >= 0 && outFile != null) {
147             try {
148                 // explicitly prevent emplacement of files accessible by outside apps
149                 mode &= 0700;
150                 Os.chmod(outFile.getPath(), (int)mode);
151             } catch (ErrnoException e) {
152                 e.rethrowAsIOException();
153             }
154             outFile.setLastModified(mtime);
155         }
156     }
157 }
158