From 5ba69740a47857fcebd36866e07963ba798269d5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 1 Mar 2013 11:18:38 -0800 Subject: Fix race conditions around filename claiming. When multiple downloads are running in parallel, they can end up claiming the same filename and clobber over each other. This change introduces locking around filename generation, and touches the claimed filename so other threads fail the File.exists() check and keep looking. Tests to verify. Bug: 8255596 Change-Id: Ie75ed047c199cf679832c75159056ca036eac18d --- src/com/android/providers/downloads/Helpers.java | 30 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'src/com/android/providers/downloads/Helpers.java') diff --git a/src/com/android/providers/downloads/Helpers.java b/src/com/android/providers/downloads/Helpers.java index 593e28b8..33205557 100644 --- a/src/com/android/providers/downloads/Helpers.java +++ b/src/com/android/providers/downloads/Helpers.java @@ -17,10 +17,6 @@ package com.android.providers.downloads; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.NetworkInfo; import android.net.Uri; import android.os.Environment; import android.os.SystemClock; @@ -29,6 +25,7 @@ import android.util.Log; import android.webkit.MimeTypeMap; import java.io.File; +import java.io.IOException; import java.util.Random; import java.util.Set; import java.util.regex.Matcher; @@ -44,6 +41,8 @@ public class Helpers { private static final Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile("attachment;\\s*filename\\s*=\\s*\"([^\"]*)\""); + private static final Object sUniqueLock = new Object(); + private Helpers() { } @@ -92,10 +91,10 @@ public class Helpers { destination); } storageManager.verifySpace(destination, path, contentLength); - path = getFullPath(path, mimeType, destination, base); if (DownloadDrmHelper.isDrmConvertNeeded(mimeType)) { path = DownloadDrmHelper.modifyDrmFwLockFileExtension(path); } + path = getFullPath(path, mimeType, destination, base); return path; } @@ -132,7 +131,21 @@ public class Helpers { if (Constants.LOGVV) { Log.v(Constants.TAG, "target file: " + filename + extension); } - return chooseUniqueFilename(destination, filename, extension, recoveryDir); + + synchronized (sUniqueLock) { + final String path = chooseUniqueFilenameLocked( + destination, filename, extension, recoveryDir); + + // Claim this filename inside lock to prevent other threads from + // clobbering us. We're not paranoid enough to use O_EXCL. + try { + new File(path).createNewFile(); + } catch (IOException e) { + throw new StopRequestException(Downloads.Impl.STATUS_FILE_ERROR, + "Failed to create target file " + path, e); + } + return path; + } } private static String chooseFilename(String url, String hint, String contentDisposition, @@ -282,11 +295,8 @@ public class Helpers { return extension; } - private static String chooseUniqueFilename(int destination, String filename, + private static String chooseUniqueFilenameLocked(int destination, String filename, String extension, boolean recoveryDir) throws StopRequestException { - // TODO: switch to actually creating the file here, otherwise we expose - // ourselves to race conditions. - String fullFilename = filename + extension; if (!new File(fullFilename).exists() && (!recoveryDir || -- cgit v1.2.3