Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: shell/application_manager/network_fetcher.cc

Issue 1011333003: Fix races when the same bits are downloaded from 2 URLs. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Follow review Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « shell/application_manager/network_fetcher.h ('k') | shell/desktop/mojo_main.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "shell/application_manager/network_fetcher.h" 5 #include "shell/application_manager/network_fetcher.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/command_line.h"
8 #include "base/files/file.h" 9 #include "base/files/file.h"
9 #include "base/files/file_path.h" 10 #include "base/files/file_path.h"
10 #include "base/files/file_util.h" 11 #include "base/files/file_util.h"
11 #include "base/message_loop/message_loop.h" 12 #include "base/message_loop/message_loop.h"
12 #include "base/process/process.h" 13 #include "base/process/process.h"
13 #include "base/stl_util.h" 14 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h" 16 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
17 #include "crypto/secure_hash.h" 18 #include "crypto/secure_hash.h"
18 #include "crypto/sha2.h" 19 #include "crypto/sha2.h"
19 #include "mojo/common/common_type_converters.h" 20 #include "mojo/common/common_type_converters.h"
20 #include "mojo/common/data_pipe_utils.h" 21 #include "mojo/common/data_pipe_utils.h"
21 #include "mojo/services/network/public/interfaces/network_service.mojom.h" 22 #include "mojo/services/network/public/interfaces/network_service.mojom.h"
22 #include "shell/application_manager/data_pipe_peek.h" 23 #include "shell/application_manager/data_pipe_peek.h"
24 #include "shell/switches.h"
23 25
24 namespace mojo { 26 namespace mojo {
25 namespace shell { 27 namespace shell {
26 28
27 NetworkFetcher::NetworkFetcher(bool disable_cache, 29 NetworkFetcher::NetworkFetcher(bool disable_cache,
28 const GURL& url, 30 const GURL& url,
29 NetworkService* network_service, 31 NetworkService* network_service,
30 const FetchCallback& loader_callback) 32 const FetchCallback& loader_callback)
31 : Fetcher(loader_callback), 33 : Fetcher(loader_callback),
32 disable_cache_(false), 34 disable_cache_(false),
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
78 // TODO(eseidel): Paths or URLs with spaces will need quoting. 80 // TODO(eseidel): Paths or URLs with spaces will need quoting.
79 std::string map_entry = 81 std::string map_entry =
80 base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str()); 82 base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
81 // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696 83 // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
82 if (!PathExists(map_path)) 84 if (!PathExists(map_path))
83 base::WriteFile(map_path, map_entry.data(), map_entry.length()); 85 base::WriteFile(map_path, map_entry.data(), map_entry.length());
84 else 86 else
85 base::AppendToFile(map_path, map_entry.data(), map_entry.length()); 87 base::AppendToFile(map_path, map_entry.data(), map_entry.length());
86 } 88 }
87 89
88 // AppIds should be be both predictable and unique, but any hash would work. 90 // For remote debugging, GDB needs to be, a apriori, aware of the filename a
89 // Currently we use sha256 from crypto/secure_hash.h 91 // library will be loaded from. AppIds should be be both predictable and unique,
92 // but any hash would work. Currently we use sha256 from crypto/secure_hash.h
90 bool NetworkFetcher::ComputeAppId(const base::FilePath& path, 93 bool NetworkFetcher::ComputeAppId(const base::FilePath& path,
91 std::string* digest_string) { 94 std::string* digest_string) {
92 scoped_ptr<crypto::SecureHash> ctx( 95 scoped_ptr<crypto::SecureHash> ctx(
93 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); 96 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
94 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ); 97 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
95 if (!file.IsValid()) { 98 if (!file.IsValid()) {
96 LOG(ERROR) << "Failed to open " << path.value() << " for computing AppId"; 99 LOG(ERROR) << "Failed to open " << path.value() << " for computing AppId";
97 return false; 100 return false;
98 } 101 }
99 char buf[1024]; 102 char buf[1024];
100 while (file.IsValid()) { 103 while (file.IsValid()) {
101 int bytes_read = file.ReadAtCurrentPos(buf, sizeof(buf)); 104 int bytes_read = file.ReadAtCurrentPos(buf, sizeof(buf));
102 if (bytes_read == 0) 105 if (bytes_read == 0)
103 break; 106 break;
104 ctx->Update(buf, bytes_read); 107 ctx->Update(buf, bytes_read);
105 } 108 }
106 if (!file.IsValid()) { 109 if (!file.IsValid()) {
107 LOG(ERROR) << "Error reading " << path.value(); 110 LOG(ERROR) << "Error reading " << path.value();
108 return false; 111 return false;
109 } 112 }
110 // The output is really a vector of unit8, we're cheating by using a string. 113 // The output is really a vector of unit8, we're cheating by using a string.
111 std::string output(crypto::kSHA256Length, 0); 114 std::string output(crypto::kSHA256Length, 0);
112 ctx->Finish(string_as_array(&output), output.size()); 115 ctx->Finish(string_as_array(&output), output.size());
113 output = base::HexEncode(output.c_str(), output.size()); 116 output = base::HexEncode(output.c_str(), output.size());
114 // Using lowercase for compatiblity with sha256sum output. 117 // Using lowercase for compatiblity with sha256sum output.
115 *digest_string = base::StringToLowerASCII(output); 118 *digest_string = base::StringToLowerASCII(output);
116 return true; 119 return true;
117 } 120 }
118 121
119 bool NetworkFetcher::RenameToAppId(const base::FilePath& old_path, 122 bool NetworkFetcher::RenameToAppId(const GURL& url,
123 const base::FilePath& old_path,
120 base::FilePath* new_path) { 124 base::FilePath* new_path) {
121 std::string app_id; 125 std::string app_id;
122 if (!ComputeAppId(old_path, &app_id)) 126 if (!ComputeAppId(old_path, &app_id))
123 return false; 127 return false;
124 128
129 // Using a hash of the url as a directory to prevent a race when the same
130 // bytes are downloaded from 2 different urls. In particular, if the same
131 // application is connected to twice concurrently with different query
132 // parameters, the directory will be different, which will prevent the
133 // collision.
134 std::string dirname = base::HexEncode(
135 crypto::SHA256HashString(url.spec()).data(), crypto::kSHA256Length);
136
125 base::FilePath temp_dir; 137 base::FilePath temp_dir;
126 base::GetTempDir(&temp_dir); 138 base::GetTempDir(&temp_dir);
139 base::FilePath app_dir = temp_dir.Append(dirname);
140 // The directory is leaked, because it can be reused at any time if the same
141 // application is downloaded. Deleting it would be racy. This is only
142 // happening when --predictable-app-filenames is used.
143 bool result = base::CreateDirectoryAndGetError(app_dir, nullptr);
144 DCHECK(result);
127 std::string unique_name = base::StringPrintf("%s.mojo", app_id.c_str()); 145 std::string unique_name = base::StringPrintf("%s.mojo", app_id.c_str());
128 *new_path = temp_dir.Append(unique_name); 146 *new_path = app_dir.Append(unique_name);
129 return base::Move(old_path, *new_path); 147 return base::Move(old_path, *new_path);
130 } 148 }
131 149
132 void NetworkFetcher::CopyCompleted( 150 void NetworkFetcher::CopyCompleted(
133 base::Callback<void(const base::FilePath&, bool)> callback, 151 base::Callback<void(const base::FilePath&, bool)> callback,
134 bool success) { 152 bool success) {
135 // The copy completed, now move to $TMP/$APP_ID.mojo before the dlopen.
136 if (success) { 153 if (success) {
137 success = false; 154 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
138 base::FilePath new_path; 155 switches::kPredictableAppFilenames)) {
139 if (RenameToAppId(path_, &new_path)) { 156 // The copy completed, now move to $TMP/$APP_ID.mojo before the dlopen.
140 if (base::PathExists(new_path)) { 157 success = false;
141 path_ = new_path; 158 base::FilePath new_path;
142 success = true; 159 if (RenameToAppId(url_, path_, &new_path)) {
143 RecordCacheToURLMapping(path_, url_); 160 if (base::PathExists(new_path)) {
161 path_ = new_path;
162 success = true;
163 }
144 } 164 }
145 } 165 }
146 } 166 }
147 167
168 if (success)
169 RecordCacheToURLMapping(path_, url_);
170
148 base::MessageLoop::current()->PostTask(FROM_HERE, 171 base::MessageLoop::current()->PostTask(FROM_HERE,
149 base::Bind(callback, path_, success)); 172 base::Bind(callback, path_, success));
150 } 173 }
151 174
152 void NetworkFetcher::AsPath( 175 void NetworkFetcher::AsPath(
153 base::TaskRunner* task_runner, 176 base::TaskRunner* task_runner,
154 base::Callback<void(const base::FilePath&, bool)> callback) { 177 base::Callback<void(const base::FilePath&, bool)> callback) {
155 if (!path_.empty() || !response_) { 178 if (!path_.empty() || !response_) {
156 base::MessageLoop::current()->PostTask( 179 base::MessageLoop::current()->PostTask(
157 FROM_HERE, base::Bind(callback, path_, base::PathExists(path_))); 180 FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
209 loader_callback_.Run(make_scoped_ptr<Fetcher>(NULL)); 232 loader_callback_.Run(make_scoped_ptr<Fetcher>(NULL));
210 return; 233 return;
211 } 234 }
212 235
213 response_ = response.Pass(); 236 response_ = response.Pass();
214 loader_callback_.Run(make_scoped_ptr(this)); 237 loader_callback_.Run(make_scoped_ptr(this));
215 } 238 }
216 239
217 } // namespace shell 240 } // namespace shell
218 } // namespace mojo 241 } // namespace mojo
OLDNEW
« no previous file with comments | « shell/application_manager/network_fetcher.h ('k') | shell/desktop/mojo_main.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698