1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_PATH_RESERVATION_TRACKER_H_
6#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_PATH_RESERVATION_TRACKER_H_
7
8#include "base/callback_forward.h"
9
10namespace base {
11class FilePath;
12}
13
14namespace content {
15class DownloadItem;
16}
17
18// Chrome attempts to uniquify filenames that are assigned to downloads in order
19// to avoid overwriting files that already exist on the file system. Downloads
20// that are considered potentially dangerous use random intermediate filenames.
21// Therefore only considering files that exist on the filesystem is
22// insufficient. This class tracks files that are assigned to active downloads
23// so that uniquification can take those into account as well.
24class DownloadPathReservationTracker {
25 public:
26  // Callback used with |GetReservedPath|. |target_path| specifies the target
27  // path for the download. |target_path_verified| is true if all of the
28  // following is true:
29  // - |requested_target_path| (passed into GetReservedPath()) was writeable.
30  // - |target_path| was verified as being unique if uniqueness was
31  //   required.
32  //
33  // If |requested_target_path| was not writeable, then the parent directory of
34  // |target_path| may be different from that of |requested_target_path|.
35  typedef base::Callback<void(const base::FilePath& target_path,
36                              bool target_path_verified)> ReservedPathCallback;
37
38  // The largest index for the uniquification suffix that we will try while
39  // attempting to come up with a unique path.
40  static const int kMaxUniqueFiles = 100;
41
42  enum FilenameConflictAction {
43    UNIQUIFY,
44    OVERWRITE,
45    PROMPT,
46  };
47
48  // When a path needs to be assigned to a download, this method is called on
49  // the UI thread along with a reference to the download item that will
50  // eventually receive the reserved path. This method creates a path
51  // reservation that will live until |download_item| is interrupted, cancelled,
52  // completes or is removed. This method will not modify |download_item|.
53  //
54  // The process of issuing a reservation happens on the FILE thread, and
55  // involves:
56  //
57  // - Creating |requested_target_path.DirName()| if it doesn't already exist
58  //   and either |create_directory| or |requested_target_path.DirName() ==
59  //   default_download_path|.
60  //
61  // - Verifying that |requested_target_path| is writeable. If not, the user's
62  //   documents folder is used instead.
63  //
64  // - Uniquifying |requested_target_path| by suffixing the filename with a
65  //   uniquifier (e.g. "foo.txt" -> "foo (1).txt") in order to avoid conflicts
66  //   with files that already exist on the file system or other download path
67  //   reservations. Uniquifying is only done if |conflict_action| is UNIQUIFY.
68  //
69  // - Posting a task back to the UI thread to invoke |completion_callback| with
70  //   the reserved path and a bool indicating whether the returned path was
71  //   verified as being writeable and unique.
72  //
73  // In addition, if the target path of |download_item| is changed to a path
74  // other than the reserved path, then the reservation will be updated to
75  // match. Such changes can happen if a "Save As" dialog was displayed and the
76  // user chose a different path. The new target path is not checked against
77  // active paths to enforce uniqueness. It is only used for uniquifying new
78  // reservations.
79  //
80  // Once |completion_callback| is invoked, it is the caller's responsibility to
81  // handle cases where the target path could not be verified and set the target
82  // path of the |download_item| appropriately.
83  //
84  // The current implementation doesn't look at symlinks/mount points. E.g.: It
85  // considers 'foo/bar/x.pdf' and 'foo/baz/x.pdf' to be two different paths,
86  // even though 'bar' might be a symlink to 'baz'.
87  static void GetReservedPath(
88      content::DownloadItem* download_item,
89      const base::FilePath& requested_target_path,
90      const base::FilePath& default_download_path,
91      bool create_directory,
92      FilenameConflictAction conflict_action,
93      const ReservedPathCallback& callback);
94
95  // Returns true if |path| is in use by an existing path reservation. Should
96  // only be called on the FILE thread. Currently only used by tests.
97  static bool IsPathInUseForTesting(const base::FilePath& path);
98};
99
100#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_PATH_RESERVATION_TRACKER_H_
101