mirror of
https://github.com/topjohnwu/libsu.git
synced 2024-11-30 07:20:27 +00:00
ShellInputStream is reliable
Stress tests show shell input is fine, the culprit is outputs
This commit is contained in:
parent
1fffc6d3f2
commit
59cab9eb84
@ -104,11 +104,6 @@ class FifoOutputStream extends BaseSuOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
|
@ -53,13 +53,11 @@ public class SuFileInputStream extends FilterInputStream {
|
||||
* On Android 5.0 and higher (API 21+), internally a named pipe (FIFO) is created
|
||||
* to bridge all I/O operations across process boundary, providing 100% native
|
||||
* {@link FileInputStream} performance.
|
||||
* A single root command is issued through the main shell.
|
||||
* A single root command is issued through the main shell at stream construction.
|
||||
* <br>
|
||||
* On Android 4.4 and lower, the provided {@code file} will first be copied into
|
||||
* the application cache folder before opening an InputStream for access.
|
||||
* The temporary file will be removed when the stream is closed.
|
||||
* <p>
|
||||
* Unlike {@link #openCompat(File)}, the stream is NOT buffered internally.
|
||||
* On Android 4.4 and lower, the returned stream will do I/O operations using {@code dd}
|
||||
* commands via the main root shell for each 4MB chunk. Due to excessive internal buffering,
|
||||
* the performance is on par with native streams.
|
||||
* @see FileInputStream#FileInputStream(File)
|
||||
*/
|
||||
public static InputStream open(File file) throws FileNotFoundException {
|
||||
@ -77,56 +75,9 @@ public class SuFileInputStream extends FilterInputStream {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code SuFileInputStream.openCompat(new File(path))}
|
||||
*/
|
||||
public static InputStream openCompat(String path) throws FileNotFoundException {
|
||||
return openCompat(new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an {@link InputStream} with root access (compatibility mode).
|
||||
* <p>
|
||||
* Unless {@code file} is an {@link SuFile}, this method will always try to directly
|
||||
* open a {@link FileInputStream}, and fallback to using root access when it fails.
|
||||
* <p>
|
||||
* <strong>Root Access Streams:</strong><br>
|
||||
* On Android 5.0 and higher (API 21+), this is the same as {@link #open(File)}, but
|
||||
* additionally wrapped with {@link BufferedInputStream} for consistency.
|
||||
* <br>
|
||||
* On Android 4.4 and lower, the returned stream will do every I/O operation with {@code dd}
|
||||
* commands via the main root shell. This was the implementation in older versions of
|
||||
* {@code libsu} and is proven to be error prone, but preserved as "compatibility mode".
|
||||
* <p>
|
||||
* The returned stream is <b>already buffered</b>, do not add another
|
||||
* layer of {@link BufferedInputStream} to add more overhead!
|
||||
* @see FileInputStream#FileInputStream(File)
|
||||
*/
|
||||
public static InputStream openCompat(File file) throws FileNotFoundException {
|
||||
if (file instanceof SuFile) {
|
||||
return shell((SuFile) file);
|
||||
} else {
|
||||
try {
|
||||
// Try normal FileInputStream
|
||||
return new BufferedInputStream(new FileInputStream(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
if (!Shell.rootAccess())
|
||||
throw e;
|
||||
return shell(new SuFile(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static InputStream root(SuFile file) throws FileNotFoundException {
|
||||
if (Build.VERSION.SDK_INT >= 21)
|
||||
return IOFactory.fifoIn(file);
|
||||
else
|
||||
return IOFactory.copyIn(file);
|
||||
}
|
||||
|
||||
private static InputStream shell(SuFile file) throws FileNotFoundException {
|
||||
if (Build.VERSION.SDK_INT >= 21)
|
||||
return new BufferedInputStream(IOFactory.fifoIn(file));
|
||||
else
|
||||
return IOFactory.shellIn(file);
|
||||
}
|
||||
@ -134,21 +85,41 @@ public class SuFileInputStream extends FilterInputStream {
|
||||
// Deprecated APIs
|
||||
|
||||
/**
|
||||
* Same as {@link #openCompat(String)}
|
||||
* Same as {@link #open(String)}, but guaranteed to be buffered internally to
|
||||
* match backwards compatibility behavior.
|
||||
* @deprecated please switch to {@link #open(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public SuFileInputStream(String path) throws FileNotFoundException {
|
||||
super(openCompat(path));
|
||||
this(new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #openCompat(File)}
|
||||
* Same as {@link #open(File)}, but guaranteed to be buffered internally to
|
||||
* match backwards compatibility behavior.
|
||||
* @deprecated please switch to {@link #open(File)}
|
||||
*/
|
||||
@Deprecated
|
||||
public SuFileInputStream(File file) throws FileNotFoundException {
|
||||
super(openCompat(file));
|
||||
super(null);
|
||||
if (file instanceof SuFile) {
|
||||
in = compat((SuFile) file);
|
||||
} else {
|
||||
try {
|
||||
// Try normal FileInputStream
|
||||
in = new BufferedInputStream(new FileInputStream(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
if (!Shell.rootAccess())
|
||||
throw e;
|
||||
in = compat(new SuFile(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static InputStream compat(SuFile file) throws FileNotFoundException {
|
||||
if (Build.VERSION.SDK_INT >= 21)
|
||||
return new BufferedInputStream(IOFactory.fifoIn(file));
|
||||
else
|
||||
return IOFactory.shellIn(file);
|
||||
}
|
||||
}
|
||||
|
@ -66,18 +66,17 @@ public class SuFileOutputStream extends BufferedOutputStream {
|
||||
* On Android 5.0 and higher (API 21+), internally a named pipe (FIFO) is created
|
||||
* to bridge all I/O operations across process boundary, providing 100% native
|
||||
* {@link FileOutputStream} performance.
|
||||
* A single root command is issued through the main shell.
|
||||
* A single root command is issued through the main shell at stream construction.
|
||||
* <br>
|
||||
* On Android 4.4 and lower, all write operations will be applied to a temporary file in
|
||||
* the application cache folder. When the stream is closed, the temporary file
|
||||
* will then be copied over to the provided {@code file} at once then deleted.
|
||||
* <p>
|
||||
* Unlike {@link #openCompat(File, boolean)}, the stream is NOT buffered internally.
|
||||
* will be copied over to the provided {@code file} by using a single {@code cat}
|
||||
* command with the main shell, then deleted.
|
||||
* @see FileOutputStream#FileOutputStream(File, boolean)
|
||||
*/
|
||||
public static OutputStream open(File file, boolean append) throws FileNotFoundException {
|
||||
if (file instanceof SuFile) {
|
||||
return root((SuFile) file, append);
|
||||
return fifo((SuFile) file, append);
|
||||
} else {
|
||||
try {
|
||||
// Try normal FileInputStream
|
||||
@ -85,55 +84,49 @@ public class SuFileOutputStream extends BufferedOutputStream {
|
||||
} catch (FileNotFoundException e) {
|
||||
if (!Shell.rootAccess())
|
||||
throw e;
|
||||
return root(new SuFile(file), append);
|
||||
return fifo(new SuFile(file), append);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code SuFileOutputStream.openCompat(new File(path), false)}
|
||||
* {@code SuFileOutputStream.openNoCopy(new File(path), false)}
|
||||
*/
|
||||
public static OutputStream openCompat(String path) throws FileNotFoundException {
|
||||
return openCompat(new File(path), false);
|
||||
public static OutputStream openNoCopy(String path) throws FileNotFoundException {
|
||||
return openNoCopy(new File(path), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code SuFileOutputStream.openCompat(new File(path), append)}
|
||||
* {@code SuFileOutputStream.openNoCopy(new File(path), append)}
|
||||
*/
|
||||
public static OutputStream openCompat(String path, boolean append) throws FileNotFoundException {
|
||||
return openCompat(new File(path), append);
|
||||
public static OutputStream openNoCopy(String path, boolean append) throws FileNotFoundException {
|
||||
return openNoCopy(new File(path), append);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code SuFileOutputStream.openCompat(file, false)}
|
||||
* {@code SuFileOutputStream.openNoCopy(file, false)}
|
||||
*/
|
||||
public static OutputStream openCompat(File file) throws FileNotFoundException {
|
||||
return openCompat(file, false);
|
||||
public static OutputStream openNoCopy(File file) throws FileNotFoundException {
|
||||
return openNoCopy(file, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an {@link OutputStream} with root access (compatibility mode).
|
||||
* Open an {@link OutputStream} with root access (no internal copying).
|
||||
* <p>
|
||||
* <strong>If your minSdkVersion is 21 or higher, this method is irrelevant.</strong>
|
||||
* <p>
|
||||
* Unless {@code file} is an {@link SuFile}, this method will always try to directly
|
||||
* open a {@link FileOutputStream}, and fallback to using root access when it fails.
|
||||
* <p>
|
||||
* <strong>Root Access Streams:</strong><br>
|
||||
* On Android 5.0 and higher (API 21+), this is the same as {@link #open(File, boolean)}, but
|
||||
* additionally wrapped with {@link BufferedOutputStream} for consistency.
|
||||
* On Android 5.0 and higher (API 21+), this is equivalent to {@link #open(File, boolean)}.
|
||||
* <br>
|
||||
* On Android 4.4 and lower, the returned stream will do every I/O operation with {@code dd}
|
||||
* commands via the main root shell. This was the implementation in older versions of
|
||||
* {@code libsu} and is proven to be error prone, but preserved as "compatibility mode".
|
||||
* <p>
|
||||
* The returned stream is <b>already buffered</b>, do not add another
|
||||
* layer of {@link BufferedOutputStream} to add more overhead!
|
||||
* On Android 4.4 and lower, the returned stream will do every write operation with a
|
||||
* {@code dd} command via the main root shell. <strong>Writing to files through shell
|
||||
* commands is proven to be error prone. YOU HAVE BEEN WARNED!</strong>
|
||||
* @see FileOutputStream#FileOutputStream(File, boolean)
|
||||
*/
|
||||
public static OutputStream openCompat(File file, boolean append) throws FileNotFoundException {
|
||||
return new BufferedOutputStream(compat(file, append));
|
||||
}
|
||||
|
||||
private static OutputStream compat(File file, boolean append) throws FileNotFoundException {
|
||||
public static OutputStream openNoCopy(File file, boolean append) throws FileNotFoundException {
|
||||
if (file instanceof SuFile) {
|
||||
return shell((SuFile) file, append);
|
||||
} else {
|
||||
@ -148,7 +141,7 @@ public class SuFileOutputStream extends BufferedOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
private static OutputStream root(SuFile file, boolean append) throws FileNotFoundException {
|
||||
private static OutputStream fifo(SuFile file, boolean append) throws FileNotFoundException {
|
||||
if (Build.VERSION.SDK_INT >= 21)
|
||||
return IOFactory.fifoOut(file, append);
|
||||
else
|
||||
@ -165,38 +158,42 @@ public class SuFileOutputStream extends BufferedOutputStream {
|
||||
// Deprecated APIs
|
||||
|
||||
/**
|
||||
* Same as {@link #openCompat(String)}
|
||||
* Same as {@link #openNoCopy(String)}, but guaranteed to be buffered internally to
|
||||
* match backwards compatibility behavior.
|
||||
* @deprecated please switch to {@link #open(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public SuFileOutputStream(String path) throws FileNotFoundException {
|
||||
this(new File(path), false);
|
||||
super(openNoCopy(path, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #openCompat(String, boolean)}
|
||||
* Same as {@link #openNoCopy(String, boolean)}, but guaranteed to be buffered internally to
|
||||
* match backwards compatibility behavior.
|
||||
* @deprecated please switch to {@link #open(String, boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public SuFileOutputStream(String path, boolean append) throws FileNotFoundException {
|
||||
this(new File(path), append);
|
||||
super(openNoCopy(path, append));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #openCompat(File)}
|
||||
* Same as {@link #openNoCopy(File)}, but guaranteed to be buffered internally to
|
||||
* match backwards compatibility behavior.
|
||||
* @deprecated please switch to {@link #open(File, boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public SuFileOutputStream(File file) throws FileNotFoundException {
|
||||
this(file, false);
|
||||
super(openNoCopy(file, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #openCompat(File, boolean)}
|
||||
* Same as {@link #openNoCopy(File, boolean)}, but guaranteed to be buffered internally to
|
||||
* match backwards compatibility behavior.
|
||||
* @deprecated please switch to {@link #open(File, boolean)}
|
||||
*/
|
||||
@Deprecated
|
||||
public SuFileOutputStream(File file, boolean append) throws FileNotFoundException {
|
||||
super(compat(file, append));
|
||||
super(openNoCopy(file, append));
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,12 @@ import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
/**
|
||||
* Access files using the global shell instance and mimics {@link RandomAccessFile}.
|
||||
* Access files using the main shell and mimics {@link RandomAccessFile}.
|
||||
* <p>
|
||||
* Usage of this class is strongly NOT recommended. Each I/O operation comes with a large
|
||||
* Usage of this class is not recommended. Each I/O operation comes with a large
|
||||
* overhead and depends on certain behavior of the command {@code dd}.
|
||||
* <strong>Writing to files through shell commands is proven to be error prone.
|
||||
* YOU HAVE BEEN WARNED!</strong>
|
||||
* Please use {@link SuFileInputStream} and {@link SuFileOutputStream} whenever possible.
|
||||
* <p>
|
||||
* This class always checks whether using a shell is necessary. If not, it simply opens a new
|
||||
|
Loading…
Reference in New Issue
Block a user