diff options
author | Jeff Sharkey <jsharkey@android.com> | 2015-06-11 19:13:37 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2015-06-11 19:16:27 -0700 |
commit | 4f5e8b3ca489245005b76176ac6d28f5f184f3fe (patch) | |
tree | 2b7975f534d07dfc7759cd5461630457b31759f5 /core | |
parent | 47b872d9c36347382dd24ad5c3f70490d8fcbb23 (diff) | |
download | frameworks_base-4f5e8b3ca489245005b76176ac6d28f5f184f3fe.zip frameworks_base-4f5e8b3ca489245005b76176ac6d28f5f184f3fe.tar.gz frameworks_base-4f5e8b3ca489245005b76176ac6d28f5f184f3fe.tar.bz2 |
Valid filenames have length limits!
ext4 filenames are at most 255 bytes. vfat filenames are bit more
lax, but we're often saving them on ext4 through a FUSE daemon, so
limit them the same way.
Since package names are used as directory names, verify that they're
valid filenames.
Tests to verify behavior.
Bug: 18689171
Change-Id: If7df4c40d352954510b71de4ff05d78259c721ed
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/content/pm/PackageParser.java | 19 | ||||
-rw-r--r-- | core/java/android/os/FileUtils.java | 26 | ||||
-rw-r--r-- | core/tests/coretests/src/android/os/FileUtilsTest.java | 12 |
3 files changed, 50 insertions, 7 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 83b0140..c92c256 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -36,6 +36,7 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.os.Build; import android.os.Bundle; +import android.os.FileUtils; import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; @@ -1194,7 +1195,8 @@ public class PackageParser { } } - private static String validateName(String name, boolean requiresSeparator) { + private static String validateName(String name, boolean requireSeparator, + boolean requireFilename) { final int N = name.length(); boolean hasSep = false; boolean front = true; @@ -1216,7 +1218,10 @@ public class PackageParser { } return "bad character '" + c + "'"; } - return hasSep || !requiresSeparator + if (requireFilename && !FileUtils.isValidExtFilename(name)) { + return "Invalid filename"; + } + return hasSep || !requireSeparator ? null : "must have at least one '.' separator"; } @@ -1240,7 +1245,7 @@ public class PackageParser { final String packageName = attrs.getAttributeValue(null, "package"); if (!"android".equals(packageName)) { - final String error = validateName(packageName, true); + final String error = validateName(packageName, true, true); if (error != null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, "Invalid manifest package: " + error); @@ -1252,7 +1257,7 @@ public class PackageParser { if (splitName.length() == 0) { splitName = null; } else { - final String error = validateName(splitName, false); + final String error = validateName(splitName, false, false); if (error != null) { throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, "Invalid manifest split: " + error); @@ -1391,7 +1396,7 @@ public class PackageParser { String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); if (str != null && str.length() > 0) { - String nameError = validateName(str, true); + String nameError = validateName(str, true, false); if (nameError != null && !"android".equals(pkgName)) { outError[0] = "<manifest> specifies bad sharedUserId name \"" + str + "\": " + nameError; @@ -1973,7 +1978,7 @@ public class PackageParser { return null; } String subName = proc.substring(1); - String nameError = validateName(subName, false); + String nameError = validateName(subName, false, false); if (nameError != null) { outError[0] = "Invalid " + type + " name " + proc + " in package " + pkg + ": " + nameError; @@ -1981,7 +1986,7 @@ public class PackageParser { } return (pkg + proc).intern(); } - String nameError = validateName(proc, true); + String nameError = validateName(proc, true, false); if (nameError != null && !"system".equals(proc)) { outError[0] = "Invalid " + type + " name " + proc + " in package " + pkg + ": " + nameError; diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 917271d..864225a 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -24,6 +24,8 @@ import android.util.Log; import android.util.Slog; import android.webkit.MimeTypeMap; +import com.android.internal.annotations.VisibleForTesting; + import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -34,6 +36,7 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; @@ -456,6 +459,7 @@ public class FileUtils { res.append('_'); } } + trimFilename(res, 255); return res.toString(); } @@ -504,9 +508,31 @@ public class FileUtils { res.append('_'); } } + // Even though vfat allows 255 UCS-2 chars, we might eventually write to + // ext4 through a FUSE layer, so use that limit. + trimFilename(res, 255); + return res.toString(); + } + + @VisibleForTesting + public static String trimFilename(String str, int maxBytes) { + final StringBuilder res = new StringBuilder(str); + trimFilename(res, maxBytes); return res.toString(); } + private static void trimFilename(StringBuilder res, int maxBytes) { + byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8); + if (raw.length > maxBytes) { + maxBytes -= 3; + while (raw.length > maxBytes) { + res.deleteCharAt(res.length() / 2); + raw = res.toString().getBytes(StandardCharsets.UTF_8); + } + res.insert(res.length() / 2, "..."); + } + } + public static String rewriteAfterRename(File beforeDir, File afterDir, String path) { if (path == null) return null; final File result = rewriteAfterRename(beforeDir, afterDir, new File(path)); diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index ee9e2e4..ac5abad 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -232,6 +232,18 @@ public class FileUtilsTest extends AndroidTestCase { assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz")); } + public void testTrimFilename() throws Exception { + assertEquals("short.txt", FileUtils.trimFilename("short.txt", 16)); + assertEquals("extrem...eme.txt", FileUtils.trimFilename("extremelylongfilename.txt", 16)); + + final String unicode = "a\u03C0\u03C0\u03C0\u03C0z"; + assertEquals("a\u03C0\u03C0\u03C0\u03C0z", FileUtils.trimFilename(unicode, 10)); + assertEquals("a\u03C0...\u03C0z", FileUtils.trimFilename(unicode, 9)); + assertEquals("a...\u03C0z", FileUtils.trimFilename(unicode, 8)); + assertEquals("a...\u03C0z", FileUtils.trimFilename(unicode, 7)); + assertEquals("a...z", FileUtils.trimFilename(unicode, 6)); + } + public void testBuildUniqueFile_normal() throws Exception { assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test")); assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg")); |