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" 111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/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" 15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h" 172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/third_party/icu/icu_utf.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/download_item.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::DownloadItem; 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 27868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)typedef DownloadItem* ReservationKey; 28868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)typedef std::map<ReservationKey, base::FilePath> ReservationMap; 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The lower bound for file name truncation. If the truncation results in a name 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// shorter than this limit, we give up automatic truncation and prompt the user. 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const size_t kTruncatedNameLengthLowerbound = 5; 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The length of the suffix string we append for an intermediate file name. 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// In the file name truncation, we keep the margin to append the suffix. 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// TODO(kinaba): remove the margin. The user should be able to set maximum 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// possible filename. 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static const size_t kIntermediateNameSuffixLength = sizeof(".crdownload") - 1; 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Map of download path reservations. Each reserved path is associated with a 41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// ReservationKey=DownloadItem*. This object is destroyed in |Revoke()| when 42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// there are no more reservations. 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 44868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// It is not an error, although undesirable, to have multiple DownloadItem*s 45868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// that are mapped to the same path. This can happen if a reservation is created 46868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// that is supposed to overwrite an existing reservation. 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ReservationMap* g_reservation_map = NULL; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Observes a DownloadItem for changes to its target path and state. Updates or 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// revokes associated download path reservations as necessary. Created, invoked 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// and destroyed on the UI thread. 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class DownloadItemObserver : public DownloadItem::Observer { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public: 54868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) explicit DownloadItemObserver(DownloadItem* download_item); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private: 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual ~DownloadItemObserver(); 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // DownloadItem::Observer 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnDownloadUpdated(DownloadItem* download) OVERRIDE; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) virtual void OnDownloadDestroyed(DownloadItem* download) OVERRIDE; 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 63868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DownloadItem* download_item_; 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Last known target path for the download. 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath last_target_path_; 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(DownloadItemObserver); 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}; 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if the given path is in use by a path reservation. 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsPathReserved(const base::FilePath& path) { 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No reservation map => no reservations. 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_reservation_map == NULL) 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Unfortunately path normalization doesn't work reliably for non-existant 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // files. So given a FilePath, we can't derive a normalized key that we can 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // use for lookups. We only expect a small number of concurrent downloads at 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // any given time, so going through all of them shouldn't be too slow. 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (ReservationMap::const_iterator iter = g_reservation_map->begin(); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter != g_reservation_map->end(); ++iter) { 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (iter->second == path) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns true if the given path is in use by any path reservation or the 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// file system. Called on the FILE thread. 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool IsPathInUse(const base::FilePath& path) { 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there is a reservation, then the path is in use. 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (IsPathReserved(path)) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the path exists in the file system, then the path is in use. 987dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (base::PathExists(path)) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Truncates path->BaseName() to make path->BaseName().value().size() <= limit. 1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// - It keeps the extension as is. Only truncates the body part. 1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// - It secures the base filename length to be more than or equals to 1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// kTruncatedNameLengthLowerbound. 1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// If it was unable to shorten the name, returns false. 1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TruncateFileName(base::FilePath* path, size_t limit) { 1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath basename(path->BaseName()); 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // It is already short enough. 1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (basename.value().size() <= limit) 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath dir(path->DirName()); 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath::StringType ext(basename.Extension()); 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath::StringType name(basename.RemoveExtension().value()); 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Impossible to satisfy the limit. 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (limit < kTruncatedNameLengthLowerbound + ext.size()) 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) limit -= ext.size(); 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Encoding specific truncation logic. 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath::StringType truncated; 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_CHROMEOS) || defined(OS_MACOSX) 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // UTF-8. 128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::TruncateUTF8ToByteSize(name, limit, &truncated); 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#elif defined(OS_WIN) 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // UTF-16. 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) DCHECK(name.size() > limit); 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) truncated = name.substr(0, CBU16_IS_TRAIL(name[limit]) ? limit - 1 : limit); 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#else 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // We cannot generally assume that the file name encoding is in UTF-8 (see 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // the comment for FilePath::AsUTF8Unsafe), hence no safe way to truncate. 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (truncated.size() < kTruncatedNameLengthLowerbound) 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *path = dir.Append(truncated + ext); 1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Called on the FILE thread to reserve a download path. This method: 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Creates directory |default_download_path| if it doesn't exist. 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Verifies that the parent directory of |suggested_path| exists and is 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// writeable. 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// - Truncates the suggested name if it exceeds the filesystem's limit. 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Uniquifies |suggested_path| if |should_uniquify_path| is true. 150f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// - Returns true if |reserved_path| has been successfully verified. 151f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)bool CreateReservation( 152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) ReservationKey key, 1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& suggested_path, 1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& default_download_path, 155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool create_directory, 156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DownloadPathReservationTracker::FilenameConflictAction conflict_action, 157f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) base::FilePath* reserved_path) { 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(suggested_path.IsAbsolute()); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create a reservation map if one doesn't exist. It will be automatically 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // deleted when all the reservations are revoked. 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_reservation_map == NULL) 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_reservation_map = new ReservationMap; 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ReservationMap& reservations = *g_reservation_map; 167868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(!ContainsKey(reservations, key)); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath target_path(suggested_path.NormalizePathSeparators()); 170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath target_dir = target_path.DirName(); 171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath filename = target_path.BaseName(); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_path_writeable = true; 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool has_conflicts = false; 1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool name_too_long = false; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Create target_dir if necessary and appropriate. target_dir may be the last 177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // directory that the user selected in a FilePicker; if that directory has 178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // since been removed, do NOT automatically re-create it. Only automatically 179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // create the directory if it is the default Downloads directory or if the 180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // caller explicitly requested automatic directory creation. 1817dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (!base::DirectoryExists(target_dir) && 182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (create_directory || 183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (!default_download_path.empty() && 184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) (default_download_path == target_dir)))) { 185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::CreateDirectory(target_dir); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Check writability of the suggested path. If we can't write to it, default 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // to the user's "My Documents" directory. We'll prompt them in this case. 1907dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch if (!base::PathIsWritable(target_dir)) { 191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) DVLOG(1) << "Unable to write to directory \"" << target_dir.value() << "\""; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_path_writeable = false; 193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) PathService::Get(chrome::DIR_USER_DOCUMENTS, &target_dir); 194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) target_path = target_dir.Append(filename); 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (is_path_writeable) { 1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Check the limit of file name length if it could be obtained. When the 1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // suggested name exceeds the limit, truncate or prompt the user. 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) int max_length = base::GetMaximumPathComponentLength(target_dir); 2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (max_length != -1) { 2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int limit = max_length - kIntermediateNameSuffixLength; 2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (limit <= 0 || !TruncateFileName(&target_path, limit)) 2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) name_too_long = true; 2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) // Uniquify the name, if it already exists. 208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!name_too_long && IsPathInUse(target_path)) { 2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) has_conflicts = true; 210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (conflict_action == DownloadPathReservationTracker::OVERWRITE) { 211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) has_conflicts = false; 212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If ...PROMPT, then |has_conflicts| will remain true, |verified| will be 214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // false, and CDMD will prompt. 215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (conflict_action == DownloadPathReservationTracker::UNIQUIFY) { 216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) for (int uniquifier = 1; 217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) uniquifier <= DownloadPathReservationTracker::kMaxUniqueFiles; 218c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) ++uniquifier) { 219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // Append uniquifier. 220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) std::string suffix(base::StringPrintf(" (%d)", uniquifier)); 221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::FilePath path_to_check(target_path); 222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If the name length limit is available (max_length != -1), and the 223c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // the current name exceeds the limit, truncate. 224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (max_length != -1) { 225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int limit = 226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) max_length - kIntermediateNameSuffixLength - suffix.size(); 227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) // If truncation failed, give up uniquification. 228c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (limit <= 0 || !TruncateFileName(&path_to_check, limit)) 229c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) break; 230c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 231c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) path_to_check = path_to_check.InsertBeforeExtensionASCII(suffix); 232c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 233c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (!IsPathInUse(path_to_check)) { 234c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) target_path = path_to_check; 235c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) has_conflicts = false; 2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) break; 237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 243868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) reservations[key] = target_path; 2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) bool verified = (is_path_writeable && !has_conflicts && !name_too_long); 245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) *reserved_path = target_path; 246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return verified; 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Called on the FILE thread to update the path of the reservation associated 250868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// with |key| to |new_path|. 251868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void UpdateReservation(ReservationKey key, const base::FilePath& new_path) { 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(g_reservation_map != NULL); 254868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) ReservationMap::iterator iter = g_reservation_map->find(key); 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (iter != g_reservation_map->end()) { 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) iter->second = new_path; 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This would happen if an UpdateReservation() notification was scheduled on 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the FILE thread before ReserveInternal(), or after a Revoke() 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // call. Neither should happen. 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Called on the FILE thread to remove the path reservation associated with 266868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)// |key|. 267868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void RevokeReservation(ReservationKey key) { 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(g_reservation_map != NULL); 270868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DCHECK(ContainsKey(*g_reservation_map, key)); 271868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) g_reservation_map->erase(key); 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (g_reservation_map->size() == 0) { 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // No more reservations. Delete map. 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete g_reservation_map; 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_reservation_map = NULL; 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 279f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void RunGetReservedPathCallback( 280f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) const DownloadPathReservationTracker::ReservedPathCallback& callback, 281f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) const base::FilePath* reserved_path, 282f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) bool verified) { 283f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 284f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) callback.Run(*reserved_path, verified); 285f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)} 286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 287868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)DownloadItemObserver::DownloadItemObserver(DownloadItem* download_item) 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : download_item_(download_item), 289868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) last_target_path_(download_item->GetTargetFilePath()) { 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 291868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) download_item_->AddObserver(this); 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DownloadItemObserver::~DownloadItemObserver() { 295868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) download_item_->RemoveObserver(this); 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DownloadItemObserver::OnDownloadUpdated(DownloadItem* download) { 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (download->GetState()) { 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::IN_PROGRESS: { 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Update the reservation. 3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::FilePath new_target_path = download->GetTargetFilePath(); 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (new_target_path != last_target_path_) { 304868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 305868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) &UpdateReservation, download, new_target_path)); 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_target_path_ = new_target_path; 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::COMPLETE: 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the download is complete, then it has already been renamed to the 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // final name. The existence of the file on disk is sufficient to prevent 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // conflicts from now on. 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::CANCELLED: 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We no longer need the reservation if the download is being removed. 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::INTERRUPTED: 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The download filename will need to be re-generated when the download is 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // restarted. Holding on to the reservation now would prevent the name 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // from being used for a subsequent retry attempt. 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 324868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 325868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) &RevokeReservation, download)); 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete this; 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case DownloadItem::MAX_DOWNLOAD_STATE: 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Compiler appeasement. 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DownloadItemObserver::OnDownloadDestroyed(DownloadItem* download) { 336868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) // Items should be COMPLETE/INTERRUPTED/CANCELLED before being destroyed. 337868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) NOTREACHED(); 338868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( 339868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) &RevokeReservation, download)); 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete this; 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DownloadPathReservationTracker::GetReservedPath( 347868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) DownloadItem* download_item, 3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& target_path, 3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& default_path, 350c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) bool create_directory, 351c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) FilenameConflictAction conflict_action, 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ReservedPathCallback& callback) { 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Attach an observer to the download item so that we know when the target 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // path changes and/or the download is no longer active. 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new DownloadItemObserver(download_item); 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // DownloadItemObserver deletes itself. 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 359f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) base::FilePath* reserved_path = new base::FilePath; 360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) BrowserThread::PostTaskAndReplyWithResult( 361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) BrowserThread::FILE, 362f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) FROM_HERE, 363f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) base::Bind(&CreateReservation, 364f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) download_item, 365f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) target_path, 366f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) default_path, 367f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) create_directory, 368f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) conflict_action, 369f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) reserved_path), 370f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) base::Bind(&RunGetReservedPathCallback, 371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) callback, 372f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) base::Owned(reserved_path))); 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool DownloadPathReservationTracker::IsPathInUseForTesting( 3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::FilePath& path) { 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return IsPathInUse(path); 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 380