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