Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02b741f8 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Restore file truncation where expected." into sc-dev

parents a4b222f4 fe738fbd
Loading
Loading
Loading
Loading
+15 −22
Original line number Diff line number Diff line
@@ -1885,9 +1885,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
     * in {@link android.provider.MediaStore.MediaColumns}.</p>
     *
     * @param uri The URI whose file is to be opened.
     * @param mode Access mode for the file.  May be "r" for read-only access,
     * "rw" for read and write access, or "rwt" for read and write access
     * that truncates any existing file.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     *
     * @return Returns a new ParcelFileDescriptor which you can use to access
     * the file.
@@ -1948,10 +1948,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
     * in {@link android.provider.MediaStore.MediaColumns}.</p>
     *
     * @param uri The URI whose file is to be opened.
     * @param mode Access mode for the file. May be "r" for read-only access,
     *            "w" for write-only access, "rw" for read and write access, or
     *            "rwt" for read and write access that truncates any existing
     *            file.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @param signal A signal to cancel the operation in progress, or
     *            {@code null} if none. For example, if you are downloading a
     *            file from the network to service a "rw" mode request, you
@@ -2011,11 +2010,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
     * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
     *
     * @param uri The URI whose file is to be opened.
     * @param mode Access mode for the file.  May be "r" for read-only access,
     * "w" for write-only access (erasing whatever data is currently in
     * the file), "wa" for write-only access to append to any existing data,
     * "rw" for read and write access on any existing data, and "rwt" for read
     * and write access that truncates any existing file.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     *
     * @return Returns a new AssetFileDescriptor which you can use to access
     * the file.
@@ -2068,11 +2065,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
     * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
     *
     * @param uri The URI whose file is to be opened.
     * @param mode Access mode for the file.  May be "r" for read-only access,
     * "w" for write-only access (erasing whatever data is currently in
     * the file), "wa" for write-only access to append to any existing data,
     * "rw" for read and write access on any existing data, and "rwt" for read
     * and write access that truncates any existing file.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @param signal A signal to cancel the operation in progress, or
     *            {@code null} if none. For example, if you are downloading a
     *            file from the network to service a "rw" mode request, you
@@ -2103,11 +2098,9 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall
     * by looking up a column named "_data" at the given URI.
     *
     * @param uri The URI to be opened.
     * @param mode The file mode.  May be "r" for read-only access,
     * "w" for write-only access (erasing whatever data is currently in
     * the file), "wa" for write-only access to append to any existing data,
     * "rw" for read and write access on any existing data, and "rwt" for read
     * and write access that truncates any existing file.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     *
     * @return Returns a new ParcelFileDescriptor that can be used by the
     * client to access the file.
+73 −13
Original line number Diff line number Diff line
@@ -63,7 +63,9 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Int64Ref;
import android.system.Os;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -76,8 +78,10 @@ import com.android.internal.util.MimeIconUtils;
import dalvik.system.CloseGuard;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -864,6 +868,20 @@ public abstract class ContentResolver implements ContentInterface {
        return wrap((ContentInterface) wrapped);
    }

    /**
     * Offer to locally truncate the given file when opened using the write-only
     * mode. This is typically used to preserve legacy compatibility behavior.
     */
    private static void maybeTruncate(FileDescriptor fd, String mode) throws FileNotFoundException {
        if ("w".equals(mode)) {
            try {
                Os.ftruncate(fd, 0);
            } catch (ErrnoException e) {
                throw new FileNotFoundException("Failed to truncate: " + e.getMessage());
            }
        }
    }

    /** @hide */
    @SuppressWarnings("HiddenAbstractMethod")
    @UnsupportedAppUsage
@@ -1525,8 +1543,20 @@ public abstract class ContentResolver implements ContentInterface {
    }

    /**
     * Synonym for {@link #openOutputStream(Uri, String)
     * openOutputStream(uri, "w")}.
     * Open a stream on to the content associated with a content URI.  If there
     * is no data associated with the URI, FileNotFoundException is thrown.
     *
     * <h5>Accepts the following URI schemes:</h5>
     * <ul>
     * <li>content ({@link #SCHEME_CONTENT})</li>
     * <li>file ({@link #SCHEME_FILE})</li>
     * </ul>
     *
     * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
     * on these schemes.
     *
     * <p>This method behaves like {@link FileOutputStream} and automatically
     * truncates any existing contents.
     *
     * @param uri The desired URI.
     * @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1534,7 +1564,16 @@ public abstract class ContentResolver implements ContentInterface {
     */
    public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
            throws FileNotFoundException {
        return openOutputStream(uri, "w");
        AssetFileDescriptor fd = openAssetFileDescriptor(uri, "w", null);
        if (fd == null) return null;
        try {
            final FileOutputStream res = fd.createOutputStream();
            // Unconditionally truncate to mirror FileOutputStream behavior
            maybeTruncate(res.getFD(), "w");
            return res;
        } catch (IOException e) {
            throw new FileNotFoundException("Unable to create stream");
        }
    }

    /**
@@ -1551,7 +1590,9 @@ public abstract class ContentResolver implements ContentInterface {
     * on these schemes.
     *
     * @param uri The desired URI.
     * @param mode May be "w", "wa", "rw", or "rwt".
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @return an OutputStream or {@code null} if the provider recently crashed.
     * @throws FileNotFoundException if the provided URI could not be opened.
     * @see #openAssetFileDescriptor(Uri, String)
@@ -1559,8 +1600,14 @@ public abstract class ContentResolver implements ContentInterface {
    public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
            throws FileNotFoundException {
        AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
        if (fd == null) return null;
        try {
            return fd != null ? fd.createOutputStream() : null;
            final FileOutputStream res = fd.createOutputStream();
            // Preserve legacy behavior by offering to truncate
            if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
                maybeTruncate(res.getFD(), mode);
            }
            return res;
        } catch (IOException e) {
            throw new FileNotFoundException("Unable to create stream");
        }
@@ -1607,8 +1654,9 @@ public abstract class ContentResolver implements ContentInterface {
     * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
     *
     * @param uri The desired URI to open.
     * @param mode The file mode to use, as per {@link ContentProvider#openFile
     * ContentProvider.openFile}.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
     * provider recently crashed. You own this descriptor and are responsible for closing it
     * when done.
@@ -1650,8 +1698,9 @@ public abstract class ContentResolver implements ContentInterface {
     * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
     *
     * @param uri The desired URI to open.
     * @param mode The file mode to use, as per {@link ContentProvider#openFile
     * ContentProvider.openFile}.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @param cancellationSignal A signal to cancel the operation in progress,
     *         or null if none. If the operation is canceled, then
     *         {@link OperationCanceledException} will be thrown.
@@ -1744,8 +1793,9 @@ public abstract class ContentResolver implements ContentInterface {
     * from any built-in data conversion that a provider implements.
     *
     * @param uri The desired URI to open.
     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
     * ContentProvider.openAssetFile}.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
     * provider recently crashed. You own this descriptor and are responsible for closing it
     * when done.
@@ -1798,8 +1848,9 @@ public abstract class ContentResolver implements ContentInterface {
     * from any built-in data conversion that a provider implements.
     *
     * @param uri The desired URI to open.
     * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
     * ContentProvider.openAssetFile}.
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt". See
     *            {@link ParcelFileDescriptor#parseMode} for more details.
     * @param cancellationSignal A signal to cancel the operation in progress, or null if
     *            none. If the operation is canceled, then
     *            {@link OperationCanceledException} will be thrown.
@@ -1835,6 +1886,10 @@ public abstract class ContentResolver implements ContentInterface {
        } else if (SCHEME_FILE.equals(scheme)) {
            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
                    new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
            // Preserve legacy behavior by offering to truncate
            if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
                maybeTruncate(pfd.getFileDescriptor(), mode);
            }
            return new AssetFileDescriptor(pfd, 0, -1);
        } else {
            if ("r".equals(mode)) {
@@ -1892,6 +1947,11 @@ public abstract class ContentResolver implements ContentInterface {
                    // ParcelFileDescriptorInner do that when it is closed.
                    stableProvider = null;

                    // Preserve legacy behavior by offering to truncate
                    if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
                        maybeTruncate(pfd.getFileDescriptor(), mode);
                    }

                    return new AssetFileDescriptor(pfd, fd.getStartOffset(),
                            fd.getDeclaredLength());

+49 −9
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static android.system.OsConstants.S_ISLNK;
import static android.system.OsConstants.S_ISREG;
import static android.system.OsConstants.S_IWOTH;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -63,6 +64,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UncheckedIOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.Socket;
import java.nio.ByteOrder;
@@ -110,6 +113,20 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {

    private final CloseGuard mGuard = CloseGuard.get();

    /** @hide */
    @IntDef(prefix = {"MODE_"}, value = {
            MODE_WORLD_READABLE,
            MODE_WORLD_WRITEABLE,
            MODE_READ_ONLY,
            MODE_WRITE_ONLY,
            MODE_READ_WRITE,
            MODE_CREATE,
            MODE_TRUNCATE,
            MODE_APPEND,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Mode { }

    /**
     * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
     * this file doesn't already exist, then create the file with permissions
@@ -227,7 +244,8 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
     *             be opened with the requested mode.
     * @see #parseMode(String)
     */
    public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
    public static ParcelFileDescriptor open(File file, @Mode int mode)
            throws FileNotFoundException {
        final FileDescriptor fd = openInternal(file, mode);
        if (fd == null) return null;

@@ -259,7 +277,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
    // We can't accept a generic Executor here, since we need to use
    // MessageQueue.addOnFileDescriptorEventListener()
    @SuppressLint("ExecutorRegistration")
    public static ParcelFileDescriptor open(File file, int mode, Handler handler,
    public static ParcelFileDescriptor open(File file, @Mode int mode, Handler handler,
            final OnCloseListener listener) throws IOException {
        if (handler == null) {
            throw new IllegalArgumentException("Handler must not be null");
@@ -330,7 +348,8 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
        return pfd;
    }

    private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
    private static FileDescriptor openInternal(File file, @Mode int mode)
            throws FileNotFoundException {
        final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);

        int realMode = S_IRWXU | S_IRWXG;
@@ -623,15 +642,36 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
    }

    /**
     * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
     * with {@link #open}.
     * Converts a string representing a file mode, such as "rw", into a bitmask
     * suitable for use with {@link #open}.
     * <p>
     * @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
     *             or "rwt".
     * The argument must define at least one of the following base access modes:
     * <ul>
     * <li>"r" indicates the file should be opened in read-only mode, equivalent
     * to {@link OsConstants#O_RDONLY}.
     * <li>"w" indicates the file should be opened in write-only mode,
     * equivalent to {@link OsConstants#O_WRONLY}.
     * <li>"rw" indicates the file should be opened in read-write mode,
     * equivalent to {@link OsConstants#O_RDWR}.
     * </ul>
     * In addition to a base access mode, the following additional modes may
     * requested:
     * <ul>
     * <li>"a" indicates the file should be opened in append mode, equivalent to
     * {@link OsConstants#O_APPEND}. Before each write, the file offset is
     * positioned at the end of the file.
     * <li>"t" indicates the file should be opened in truncate mode, equivalent
     * to {@link OsConstants#O_TRUNC}. If the file already exists and is a
     * regular file and is opened for writing, it will be truncated to length 0.
     * </ul>
     *
     * @param mode The string representation of the file mode. Can be "r", "w",
     *            "wt", "wa", "rw" or "rwt".
     * @return A bitmask representing the given file mode.
     * @throws IllegalArgumentException if the given string does not match a known file mode.
     * @throws IllegalArgumentException if the given string does not match a
     *             known file mode.
     */
    public static int parseMode(String mode) {
    public static @Mode int parseMode(String mode) {
        return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
    }