download_path_reservation_tracker.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/download/download_path_reservation_tracker.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <map> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/callback.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h" 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/string_util.h" 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/stringprintf.h" 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/third_party/icu/icu_utf.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/download/download_util.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/download_id.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/download_item.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread; 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::DownloadId; 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::DownloadItem; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)typedef std::map<content::DownloadId, base::FilePath> ReservationMap; 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The lower bound for file name truncation. If the truncation results in a name 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// shorter than this limit, we give up automatic truncation and prompt the user. 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const size_t kTruncatedNameLengthLowerbound = 5; 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The length of the suffix string we append for an intermediate file name. 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// In the file name truncation, we keep the margin to append the suffix. 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(kinaba): remove the margin. The user should be able to set maximum 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// possible filename. 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const size_t kIntermediateNameSuffixLength = sizeof(".crdownload") - 1; 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Map of download path reservations. Each reserved path is associated with a 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// DownloadId. This object is destroyed in |Revoke()| when there are no more 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// reservations. 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// It is not an error, although undesirable, to have multiple DownloadIds that 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// are mapped to the same path. This can happen if a reservation is created that 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// is supposed to overwrite an existing reservation. 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ReservationMap* g_reservation_map = NULL; 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Observes a DownloadItem for changes to its target path and state. Updates or 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// revokes associated download path reservations as necessary. Created, invoked 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and destroyed on the UI thread. 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class DownloadItemObserver : public DownloadItem::Observer { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) explicit DownloadItemObserver(DownloadItem& download_item); 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual ~DownloadItemObserver(); 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // DownloadItem::Observer 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE; 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE; 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DownloadItem& download_item_; 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Last known target path for the download. 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath last_target_path_; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(DownloadItemObserver); 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if the given path is in use by a path reservation. 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsPathReserved(const base::FilePath& path) { 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No reservation map => no reservations. 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_reservation_map == NULL) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Unfortunately path normalization doesn't work reliably for non-existant 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // files. So given a FilePath, we can't derive a normalized key that we can 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // use for lookups. We only expect a small number of concurrent downloads at 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // any given time, so going through all of them shouldn't be too slow. 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (ReservationMap::const_iterator iter = g_reservation_map->begin(); 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter != g_reservation_map->end(); ++iter) { 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (iter->second == path) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if the given path is in use by any path reservation or the 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// file system. Called on the FILE thread. 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsPathInUse(const base::FilePath& path) { 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there is a reservation, then the path is in use. 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsPathReserved(path)) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the path exists in the file system, then the path is in use. 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (file_util::PathExists(path)) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Truncates path->BaseName() to make path->BaseName().value().size() <= limit. 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// - It keeps the extension as is. Only truncates the body part. 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// - It secures the base filename length to be more than or equals to 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// kTruncatedNameLengthLowerbound. 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If it was unable to shorten the name, returns false. 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TruncateFileName(base::FilePath* path, size_t limit) { 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath basename(path->BaseName()); 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // It is already short enough. 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (basename.value().size() <= limit) 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath dir(path->DirName()); 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath::StringType ext(basename.Extension()); 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath::StringType name(basename.RemoveExtension().value()); 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Impossible to satisfy the limit. 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (limit < kTruncatedNameLengthLowerbound + ext.size()) 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) limit -= ext.size(); 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Encoding specific truncation logic. 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath::StringType truncated; 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_CHROMEOS) || defined(OS_MACOSX) 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // UTF-8. 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) TruncateUTF8ToByteSize(name, limit, &truncated); 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#elif defined(OS_WIN) 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // UTF-16. 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(name.size() > limit); 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) truncated = name.substr(0, CBU16_IS_TRAIL(name[limit]) ? limit - 1 : limit); 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#else 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // We cannot generally assume that the file name encoding is in UTF-8 (see 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // the comment for FilePath::AsUTF8Unsafe), hence no safe way to truncate. 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (truncated.size() < kTruncatedNameLengthLowerbound) 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *path = dir.Append(truncated + ext); 1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Called on the FILE thread to reserve a download path. This method: 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Creates directory |default_download_path| if it doesn't exist. 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Verifies that the parent directory of |suggested_path| exists and is 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// writeable. 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// - Truncates the suggested name if it exceeds the filesystem's limit. 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Uniquifies |suggested_path| if |should_uniquify_path| is true. 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Schedules |callback| on the UI thread with the reserved path and a flag 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// indicating whether the returned path has been successfully verified. 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void CreateReservation( 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DownloadId download_id, 1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& suggested_path, 1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& default_download_path, 158c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool create_directory, 159c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DownloadPathReservationTracker::FilenameConflictAction conflict_action, 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const DownloadPathReservationTracker::ReservedPathCallback& callback) { 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(download_id.IsValid()); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(suggested_path.IsAbsolute()); 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create a reservation map if one doesn't exist. It will be automatically 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // deleted when all the reservations are revoked. 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_reservation_map == NULL) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_reservation_map = new ReservationMap; 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReservationMap& reservations = *g_reservation_map; 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!ContainsKey(reservations, download_id)); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath target_path(suggested_path.NormalizePathSeparators()); 174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath target_dir = target_path.DirName(); 175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath filename = target_path.BaseName(); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_path_writeable = true; 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_conflicts = false; 1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool name_too_long = false; 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Create target_dir if necessary and appropriate. target_dir may be the last 181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // directory that the user selected in a FilePicker; if that directory has 182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // since been removed, do NOT automatically re-create it. Only automatically 183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // create the directory if it is the default Downloads directory or if the 184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // caller explicitly requested automatic directory creation. 185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!file_util::DirectoryExists(target_dir) && 186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (create_directory || 187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (!default_download_path.empty() && 188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (default_download_path == target_dir)))) { 189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) file_util::CreateDirectory(target_dir); 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check writability of the suggested path. If we can't write to it, default 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to the user's "My Documents" directory. We'll prompt them in this case. 194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!file_util::PathIsWritable(target_dir)) { 195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DVLOG(1) << "Unable to write to directory \"" << target_dir.value() << "\""; 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_path_writeable = false; 197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) PathService::Get(chrome::DIR_USER_DOCUMENTS, &target_dir); 198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) target_path = target_dir.Append(filename); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (is_path_writeable) { 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Check the limit of file name length if it could be obtained. When the 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // suggested name exceeds the limit, truncate or prompt the user. 204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int max_length = file_util::GetMaximumPathComponentLength(target_dir); 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (max_length != -1) { 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int limit = max_length - kIntermediateNameSuffixLength; 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (limit <= 0 || !TruncateFileName(&target_path, limit)) 2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) name_too_long = true; 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Uniquify the name, if it already exists. 212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!name_too_long && IsPathInUse(target_path)) { 2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) has_conflicts = true; 214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (conflict_action == DownloadPathReservationTracker::OVERWRITE) { 215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) has_conflicts = false; 216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If ...PROMPT, then |has_conflicts| will remain true, |verified| will be 218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // false, and CDMD will prompt. 219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (conflict_action == DownloadPathReservationTracker::UNIQUIFY) { 220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) for (int uniquifier = 1; 221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles; 222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ++uniquifier) { 223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Append uniquifier. 224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string suffix(base::StringPrintf(" (%d)", uniquifier)); 225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath path_to_check(target_path); 226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If the name length limit is available (max_length != -1), and the 227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // the current name exceeds the limit, truncate. 228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (max_length != -1) { 229c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int limit = 230c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) max_length - kIntermediateNameSuffixLength - suffix.size(); 231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If truncation failed, give up uniquification. 232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (limit <= 0 || !TruncateFileName(&path_to_check, limit)) 233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) break; 234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) path_to_check = path_to_check.InsertBeforeExtensionASCII(suffix); 236c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!IsPathInUse(path_to_check)) { 238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) target_path = path_to_check; 239c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) has_conflicts = false; 2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 241c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) reservations[download_id] = target_path; 2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool verified = (is_path_writeable && !has_conflicts && !name_too_long); 2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::Bind(callback, target_path, verified)); 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Called on the FILE thread to update the path of the reservation associated 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// with |download_id| to |new_path|. 2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void UpdateReservation(DownloadId download_id, const base::FilePath& new_path) { 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(g_reservation_map != NULL); 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReservationMap::iterator iter = g_reservation_map->find(download_id); 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (iter != g_reservation_map->end()) { 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter->second = new_path; 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This would happen if an UpdateReservation() notification was scheduled on 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the FILE thread before ReserveInternal(), or after a Revoke() 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // call. Neither should happen. 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Called on the FILE thread to remove the path reservation associated with 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// |download_id|. 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void RevokeReservation(DownloadId download_id) { 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(g_reservation_map != NULL); 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(ContainsKey(*g_reservation_map, download_id)); 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_reservation_map->erase(download_id); 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_reservation_map->size() == 0) { 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No more reservations. Delete map. 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete g_reservation_map; 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_reservation_map = NULL; 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DownloadItemObserver::DownloadItemObserver(DownloadItem& download_item) 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : download_item_(download_item), 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_target_path_(download_item.GetTargetFilePath()) { 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) download_item_.AddObserver(this); 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DownloadItemObserver::~DownloadItemObserver() { 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) download_item_.RemoveObserver(this); 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DownloadItemObserver::OnDownloadUpdated(DownloadItem* download) { 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (download->GetState()) { 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::IN_PROGRESS: { 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update the reservation. 2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath new_target_path = download->GetTargetFilePath(); 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (new_target_path != last_target_path_) { 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserThread::PostTask( 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserThread::FILE, FROM_HERE, 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&UpdateReservation, download->GetGlobalId(), 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new_target_path)); 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_target_path_ = new_target_path; 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::COMPLETE: 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the download is complete, then it has already been renamed to the 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // final name. The existence of the file on disk is sufficient to prevent 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // conflicts from now on. 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::CANCELLED: 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We no longer need the reservation if the download is being removed. 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::INTERRUPTED: 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The download filename will need to be re-generated when the download is 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // restarted. Holding on to the reservation now would prevent the name 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // from being used for a subsequent retry attempt. 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserThread::PostTask( 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserThread::FILE, FROM_HERE, 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&RevokeReservation, download->GetGlobalId())); 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete this; 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::MAX_DOWNLOAD_STATE: 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Compiler appeasement. 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DownloadItemObserver::OnDownloadDestroyed(DownloadItem* download) { 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This shouldn't happen. We should catch either COMPLETE, CANCELLED, or 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // INTERRUPTED first. 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Bind(&RevokeReservation, download->GetGlobalId())); 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete this; 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DownloadPathReservationTracker::GetReservedPath( 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DownloadItem& download_item, 3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& target_path, 3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& default_path, 349c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool create_directory, 350c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) FilenameConflictAction conflict_action, 3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ReservedPathCallback& callback) { 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Attach an observer to the download item so that we know when the target 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // path changes and/or the download is no longer active. 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new DownloadItemObserver(download_item); 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // DownloadItemObserver deletes itself. 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 358c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 359c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) &CreateReservation, 360c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) download_item.GetGlobalId(), 361c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) target_path, 362c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) default_path, 363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) create_directory, 364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) conflict_action, 365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) callback)); 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool DownloadPathReservationTracker::IsPathInUseForTesting( 3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& path) { 3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return IsPathInUse(path); 3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 373