1 /*
2  * Copyright (C) 2014 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.os;
18 
19 import static android.system.OsConstants.AF_UNIX;
20 import static android.system.OsConstants.SOCK_STREAM;
21 
22 import android.system.ErrnoException;
23 import android.system.Os;
24 import android.util.Log;
25 
26 import libcore.io.IoBridge;
27 import libcore.io.IoUtils;
28 import libcore.io.Memory;
29 import libcore.io.Streams;
30 import libcore.util.ArrayUtils;
31 
32 import java.io.FileDescriptor;
33 import java.io.IOException;
34 import java.io.OutputStream;
35 import java.nio.ByteOrder;
36 
37 /**
38  * Simple bridge that allows file access across process boundaries without
39  * returning the underlying {@link FileDescriptor}. This is useful when the
40  * server side needs to strongly assert that a client side is completely
41  * hands-off.
42  *
43  * @hide
44  * @deprecated replaced by {@link RevocableFileDescriptor}
45  */
46 @Deprecated
47 public class FileBridge extends Thread {
48     private static final String TAG = "FileBridge";
49 
50     // TODO: consider extending to support bidirectional IO
51 
52     private static final int MSG_LENGTH = 8;
53 
54     /** CMD_WRITE [len] [data] */
55     private static final int CMD_WRITE = 1;
56     /** CMD_FSYNC */
57     private static final int CMD_FSYNC = 2;
58     /** CMD_CLOSE */
59     private static final int CMD_CLOSE = 3;
60 
61     private FileDescriptor mTarget;
62 
63     private final FileDescriptor mServer = new FileDescriptor();
64     private final FileDescriptor mClient = new FileDescriptor();
65 
66     private volatile boolean mClosed;
67 
FileBridge()68     public FileBridge() {
69         try {
70             Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
71         } catch (ErrnoException e) {
72             throw new RuntimeException("Failed to create bridge");
73         }
74     }
75 
isClosed()76     public boolean isClosed() {
77         return mClosed;
78     }
79 
forceClose()80     public void forceClose() {
81         IoUtils.closeQuietly(mTarget);
82         IoUtils.closeQuietly(mServer);
83         IoUtils.closeQuietly(mClient);
84         mClosed = true;
85     }
86 
setTargetFile(FileDescriptor target)87     public void setTargetFile(FileDescriptor target) {
88         mTarget = target;
89     }
90 
getClientSocket()91     public FileDescriptor getClientSocket() {
92         return mClient;
93     }
94 
95     @Override
run()96     public void run() {
97         final byte[] temp = new byte[8192];
98         try {
99             while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
100                 final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
101                 if (cmd == CMD_WRITE) {
102                     // Shuttle data into local file
103                     int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
104                     while (len > 0) {
105                         int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
106                         if (n == -1) {
107                             throw new IOException(
108                                     "Unexpected EOF; still expected " + len + " bytes");
109                         }
110                         IoBridge.write(mTarget, temp, 0, n);
111                         len -= n;
112                     }
113 
114                 } else if (cmd == CMD_FSYNC) {
115                     // Sync and echo back to confirm
116                     Os.fsync(mTarget);
117                     IoBridge.write(mServer, temp, 0, MSG_LENGTH);
118 
119                 } else if (cmd == CMD_CLOSE) {
120                     // Close and echo back to confirm
121                     Os.fsync(mTarget);
122                     Os.close(mTarget);
123                     mClosed = true;
124                     IoBridge.write(mServer, temp, 0, MSG_LENGTH);
125                     break;
126                 }
127             }
128 
129         } catch (ErrnoException | IOException e) {
130             Log.wtf(TAG, "Failed during bridge", e);
131         } finally {
132             forceClose();
133         }
134     }
135 
136     public static class FileBridgeOutputStream extends OutputStream {
137         private final ParcelFileDescriptor mClientPfd;
138         private final FileDescriptor mClient;
139         private final byte[] mTemp = new byte[MSG_LENGTH];
140 
FileBridgeOutputStream(ParcelFileDescriptor clientPfd)141         public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
142             mClientPfd = clientPfd;
143             mClient = clientPfd.getFileDescriptor();
144         }
145 
FileBridgeOutputStream(FileDescriptor client)146         public FileBridgeOutputStream(FileDescriptor client) {
147             mClientPfd = null;
148             mClient = client;
149         }
150 
151         @Override
close()152         public void close() throws IOException {
153             try {
154                 writeCommandAndBlock(CMD_CLOSE, "close()");
155             } finally {
156                 IoBridge.closeAndSignalBlockedThreads(mClient);
157                 IoUtils.closeQuietly(mClientPfd);
158             }
159         }
160 
fsync()161         public void fsync() throws IOException {
162             writeCommandAndBlock(CMD_FSYNC, "fsync()");
163         }
164 
writeCommandAndBlock(int cmd, String cmdString)165         private void writeCommandAndBlock(int cmd, String cmdString) throws IOException {
166             Memory.pokeInt(mTemp, 0, cmd, ByteOrder.BIG_ENDIAN);
167             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
168 
169             // Wait for server to ack
170             if (IoBridge.read(mClient, mTemp, 0, MSG_LENGTH) == MSG_LENGTH) {
171                 if (Memory.peekInt(mTemp, 0, ByteOrder.BIG_ENDIAN) == cmd) {
172                     return;
173                 }
174             }
175 
176             throw new IOException("Failed to execute " + cmdString + " across bridge");
177         }
178 
179         @Override
write(byte[] buffer, int byteOffset, int byteCount)180         public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
181             ArrayUtils.throwsIfOutOfBounds(buffer.length, byteOffset, byteCount);
182             Memory.pokeInt(mTemp, 0, CMD_WRITE, ByteOrder.BIG_ENDIAN);
183             Memory.pokeInt(mTemp, 4, byteCount, ByteOrder.BIG_ENDIAN);
184             IoBridge.write(mClient, mTemp, 0, MSG_LENGTH);
185             IoBridge.write(mClient, buffer, byteOffset, byteCount);
186         }
187 
188         @Override
write(int oneByte)189         public void write(int oneByte) throws IOException {
190             Streams.writeSingleByte(this, oneByte);
191         }
192     }
193 }
194