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