blob: fcaae1ab280283f0bc8f4fb98288ef76a9d77730 [file] [log] [blame]
David Zeuthen27a48bc2013-08-06 12:06:29 -07001// Copyright (c) 2013 The Chromium OS 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
Alex Vakulenko072359c2014-07-18 11:41:07 -07005// This provides access to timestamps with nanosecond resolution in
David Zeuthen27a48bc2013-08-06 12:06:29 -07006// struct stat, See NOTES in stat(2) for details.
7#ifndef _BSD_SOURCE
8#define _BSD_SOURCE
9#endif
10
11#include "update_engine/p2p_manager.h"
12
13#include <attr/xattr.h>
14#include <dirent.h>
15#include <errno.h>
16#include <fcntl.h>
17#include <glib.h>
18#include <linux/falloc.h>
19#include <signal.h>
20#include <string.h>
21#include <sys/stat.h>
22#include <sys/statvfs.h>
23#include <sys/types.h>
24#include <unistd.h>
David Zeuthen27a48bc2013-08-06 12:06:29 -070025
Alex Vakulenkod2779df2014-06-16 13:19:00 -070026#include <algorithm>
David Zeuthen27a48bc2013-08-06 12:06:29 -070027#include <map>
28#include <utility>
29#include <vector>
30
Alex Vakulenko75039d72014-03-25 12:36:28 -070031#include <base/files/file_path.h>
David Zeuthen27a48bc2013-08-06 12:06:29 -070032#include <base/logging.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070033#include <base/strings/stringprintf.h>
David Zeuthen27a48bc2013-08-06 12:06:29 -070034
Alex Deymo44666f92014-07-22 20:29:24 -070035#include "update_engine/glib_utils.h"
David Zeuthen27a48bc2013-08-06 12:06:29 -070036#include "update_engine/utils.h"
37
38using base::FilePath;
39using base::StringPrintf;
40using base::Time;
41using base::TimeDelta;
42using std::map;
43using std::pair;
44using std::string;
45using std::vector;
46
47namespace chromeos_update_engine {
48
49namespace {
50
51// The default p2p directory.
52const char kDefaultP2PDir[] = "/var/cache/p2p";
53
54// The p2p xattr used for conveying the final size of a file - see the
55// p2p ddoc for details.
56const char kCrosP2PFileSizeXAttrName[] = "user.cros-p2p-filesize";
57
Alex Vakulenkod2779df2014-06-16 13:19:00 -070058} // namespace
David Zeuthen27a48bc2013-08-06 12:06:29 -070059
60// The default P2PManager::Configuration implementation.
61class ConfigurationImpl : public P2PManager::Configuration {
Alex Vakulenkod2779df2014-06-16 13:19:00 -070062 public:
David Zeuthen27a48bc2013-08-06 12:06:29 -070063 ConfigurationImpl() {}
64
65 virtual ~ConfigurationImpl() {}
66
Alex Vakulenko75039d72014-03-25 12:36:28 -070067 virtual base::FilePath GetP2PDir() {
68 return base::FilePath(kDefaultP2PDir);
David Zeuthen27a48bc2013-08-06 12:06:29 -070069 }
70
71 virtual vector<string> GetInitctlArgs(bool is_start) {
72 vector<string> args;
73 args.push_back("initctl");
74 args.push_back(is_start ? "start" : "stop");
75 args.push_back("p2p");
76 return args;
77 }
78
79 virtual vector<string> GetP2PClientArgs(const string &file_id,
80 size_t minimum_size) {
81 vector<string> args;
82 args.push_back("p2p-client");
83 args.push_back(string("--get-url=") + file_id);
Alex Vakulenko75039d72014-03-25 12:36:28 -070084 args.push_back(base::StringPrintf("--minimum-size=%zu", minimum_size));
David Zeuthen27a48bc2013-08-06 12:06:29 -070085 return args;
86 }
87
Alex Vakulenkod2779df2014-06-16 13:19:00 -070088 private:
David Zeuthen27a48bc2013-08-06 12:06:29 -070089 DISALLOW_COPY_AND_ASSIGN(ConfigurationImpl);
90};
91
92// The default P2PManager implementation.
93class P2PManagerImpl : public P2PManager {
Alex Vakulenkod2779df2014-06-16 13:19:00 -070094 public:
David Zeuthen27a48bc2013-08-06 12:06:29 -070095 P2PManagerImpl(Configuration *configuration,
96 PrefsInterface *prefs,
97 const string& file_extension,
98 const int num_files_to_keep);
99
100 // P2PManager methods.
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700101 virtual void SetDevicePolicy(const policy::DevicePolicy* device_policy);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700102 virtual bool IsP2PEnabled();
103 virtual bool EnsureP2PRunning();
104 virtual bool EnsureP2PNotRunning();
105 virtual bool PerformHousekeeping();
106 virtual void LookupUrlForFile(const string& file_id,
107 size_t minimum_size,
108 TimeDelta max_time_to_wait,
109 LookupCallback callback);
110 virtual bool FileShare(const string& file_id,
111 size_t expected_size);
Alex Vakulenko75039d72014-03-25 12:36:28 -0700112 virtual base::FilePath FileGetPath(const string& file_id);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700113 virtual ssize_t FileGetSize(const string& file_id);
114 virtual ssize_t FileGetExpectedSize(const string& file_id);
115 virtual bool FileGetVisible(const string& file_id,
116 bool *out_result);
117 virtual bool FileMakeVisible(const string& file_id);
118 virtual int CountSharedFiles();
119
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700120 private:
David Zeuthen27a48bc2013-08-06 12:06:29 -0700121 // Enumeration for specifying visibility.
122 enum Visibility {
123 kVisible,
124 kNonVisible
125 };
126
127 // Returns "." + |file_extension_| + ".p2p" if |visibility| is
128 // |kVisible|. Returns the same concatenated with ".tmp" otherwise.
129 string GetExt(Visibility visibility);
130
131 // Gets the on-disk path for |file_id| depending on if the file
132 // is visible or not.
Alex Vakulenko75039d72014-03-25 12:36:28 -0700133 base::FilePath GetPath(const string& file_id, Visibility visibility);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700134
135 // Utility function used by EnsureP2PRunning() and EnsureP2PNotRunning().
136 bool EnsureP2P(bool should_be_running);
137
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700138 // The device policy being used or null if no policy is being used.
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700139 const policy::DevicePolicy* device_policy_;
140
David Zeuthen27a48bc2013-08-06 12:06:29 -0700141 // Configuration object.
142 scoped_ptr<Configuration> configuration_;
143
144 // Object for persisted state.
145 PrefsInterface* prefs_;
146
147 // A short string unique to the application (for example "cros_au")
148 // used to mark a file as being owned by a particular application.
149 const string file_extension_;
150
151 // If non-zero, this number denotes how many files in /var/cache/p2p
152 // owned by the application (cf. |file_extension_|) to keep after
153 // performing housekeeping.
154 const int num_files_to_keep_;
155
156 // The string ".p2p".
157 static const char kP2PExtension[];
158
159 // The string ".tmp".
160 static const char kTmpExtension[];
161
162 DISALLOW_COPY_AND_ASSIGN(P2PManagerImpl);
163};
164
165const char P2PManagerImpl::kP2PExtension[] = ".p2p";
166
167const char P2PManagerImpl::kTmpExtension[] = ".tmp";
168
169P2PManagerImpl::P2PManagerImpl(Configuration *configuration,
170 PrefsInterface *prefs,
171 const string& file_extension,
172 const int num_files_to_keep)
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700173 : device_policy_(nullptr),
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700174 prefs_(prefs),
David Zeuthen27a48bc2013-08-06 12:06:29 -0700175 file_extension_(file_extension),
176 num_files_to_keep_(num_files_to_keep) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700177 configuration_.reset(configuration != nullptr ? configuration :
David Zeuthen27a48bc2013-08-06 12:06:29 -0700178 new ConfigurationImpl());
179}
180
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700181void P2PManagerImpl::SetDevicePolicy(
182 const policy::DevicePolicy* device_policy) {
183 device_policy_ = device_policy;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700184}
185
186bool P2PManagerImpl::IsP2PEnabled() {
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700187 bool p2p_enabled = false;
188
189 // The logic we want here is additive, e.g. p2p can be enabled by
190 // either the crosh flag OR by Enterprise Policy, e.g. the following
191 // truth table:
192 //
193 // crosh_flag == FALSE && enterprise_policy == FALSE -> use_p2p == FALSE
194 // crosh_flag == FALSE && enterprise_policy == TRUE -> use_p2p == TRUE
195 // crosh_flag == TRUE && enterprise_policy == FALSE -> use_p2p == TRUE
196 // crosh_flag == TRUE && enterprise_policy == TRUE -> use_p2p == TRUE
197
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700198 if (device_policy_ != nullptr) {
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700199 if (device_policy_->GetAuP2PEnabled(&p2p_enabled)) {
200 if (p2p_enabled) {
201 LOG(INFO) << "Enterprise Policy indicates that p2p is enabled.";
202 return true;
203 }
204 }
David Zeuthen27a48bc2013-08-06 12:06:29 -0700205 }
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700206
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700207 if (prefs_ != nullptr &&
David Zeuthen92d9c8b2013-09-11 10:58:11 -0700208 prefs_->Exists(kPrefsP2PEnabled) &&
209 prefs_->GetBoolean(kPrefsP2PEnabled, &p2p_enabled) &&
210 p2p_enabled) {
211 LOG(INFO) << "The crosh flag indicates that p2p is enabled.";
212 return true;
213 }
214
215 LOG(INFO) << "Neither Enterprise Policy nor crosh flag indicates that p2p "
216 << "is enabled.";
217 return false;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700218}
219
220bool P2PManagerImpl::EnsureP2P(bool should_be_running) {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700221 gchar *standard_error = nullptr;
222 GError *error = nullptr;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700223 gint exit_status = 0;
224
225 vector<string> args = configuration_->GetInitctlArgs(should_be_running);
226 scoped_ptr<gchar*, GLibStrvFreeDeleter> argv(
227 utils::StringVectorToGStrv(args));
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700228 if (!g_spawn_sync(nullptr, // working_directory
David Zeuthen27a48bc2013-08-06 12:06:29 -0700229 argv.get(),
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700230 nullptr, // envp
David Zeuthen27a48bc2013-08-06 12:06:29 -0700231 static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH),
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700232 nullptr, nullptr, // child_setup, user_data
233 nullptr, // standard_output
David Zeuthen27a48bc2013-08-06 12:06:29 -0700234 &standard_error,
235 &exit_status,
236 &error)) {
237 LOG(ERROR) << "Error spawning " << utils::StringVectorToString(args)
238 << ": " << utils::GetAndFreeGError(&error);
239 return false;
240 }
241 scoped_ptr<gchar, GLibFreeDeleter> standard_error_deleter(standard_error);
242
243 if (!WIFEXITED(exit_status)) {
244 LOG(ERROR) << "Error spawning '" << utils::StringVectorToString(args)
245 << "': WIFEXITED is false";
246 return false;
247 }
248
249 // If initctl(8) exits normally with exit status 0 ("success"), it
250 // meant that it did what we requested.
251 if (WEXITSTATUS(exit_status) == 0) {
252 return true;
253 }
254
255 // Otherwise, screenscape stderr from initctl(8). Ugh, yes, this is
256 // ugly but since the program lacks verbs/actions such as
257 //
258 // ensure-started (or start-or-return-success-if-already-started)
259 // ensure-stopped (or stop-or-return-success-if-not-running)
260 //
261 // this is what we have to do.
262 //
263 // TODO(zeuthen,chromium:277051): Avoid doing this.
264 const gchar *expected_error_message = should_be_running ?
265 "initctl: Job is already running: p2p\n" :
266 "initctl: Unknown instance \n";
267 if (g_strcmp0(standard_error, expected_error_message) == 0) {
268 return true;
269 }
270
271 return false;
272}
273
274bool P2PManagerImpl::EnsureP2PRunning() {
275 return EnsureP2P(true);
276}
277
278bool P2PManagerImpl::EnsureP2PNotRunning() {
279 return EnsureP2P(false);
280}
281
282// Returns True if the timestamp in the first pair is greater than the
283// timestamp in the latter. If used with std::sort() this will yield a
284// sequence of elements where newer (high timestamps) elements precede
285// older ones (low timestamps).
286static bool MatchCompareFunc(const pair<FilePath, Time>& a,
287 const pair<FilePath, Time>& b) {
288 return a.second > b.second;
289}
290
291string P2PManagerImpl::GetExt(Visibility visibility) {
292 string ext = string(".") + file_extension_ + kP2PExtension;
293 switch (visibility) {
294 case kVisible:
295 break;
296 case kNonVisible:
297 ext += kTmpExtension;
298 break;
299 // Don't add a default case to let the compiler warn about newly
300 // added enum values.
301 }
302 return ext;
303}
304
305FilePath P2PManagerImpl::GetPath(const string& file_id, Visibility visibility) {
306 return configuration_->GetP2PDir().Append(file_id + GetExt(visibility));
307}
308
309bool P2PManagerImpl::PerformHousekeeping() {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700310 GDir* dir = nullptr;
311 GError* error = nullptr;
312 const char* name = nullptr;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700313 vector<pair<FilePath, Time> > matches;
314
315 // Go through all files in the p2p dir and pick the ones that match
316 // and get their ctime.
Alex Vakulenko75039d72014-03-25 12:36:28 -0700317 base::FilePath p2p_dir = configuration_->GetP2PDir();
David Zeuthen27a48bc2013-08-06 12:06:29 -0700318 dir = g_dir_open(p2p_dir.value().c_str(), 0, &error);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700319 if (dir == nullptr) {
David Zeuthen27a48bc2013-08-06 12:06:29 -0700320 LOG(ERROR) << "Error opening directory " << p2p_dir.value() << ": "
321 << utils::GetAndFreeGError(&error);
322 return false;
323 }
324
325 if (num_files_to_keep_ == 0)
326 return true;
327
328 string ext_visible = GetExt(kVisible);
329 string ext_non_visible = GetExt(kNonVisible);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700330 while ((name = g_dir_read_name(dir)) != nullptr) {
David Zeuthen27a48bc2013-08-06 12:06:29 -0700331 if (!(g_str_has_suffix(name, ext_visible.c_str()) ||
332 g_str_has_suffix(name, ext_non_visible.c_str())))
333 continue;
334
335 struct stat statbuf;
Alex Vakulenko75039d72014-03-25 12:36:28 -0700336 base::FilePath file = p2p_dir.Append(name);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700337 if (stat(file.value().c_str(), &statbuf) != 0) {
338 PLOG(ERROR) << "Error getting file status for " << file.value();
339 continue;
340 }
341
342 Time time = utils::TimeFromStructTimespec(&statbuf.st_ctim);
343 matches.push_back(std::make_pair(file, time));
344 }
345 g_dir_close(dir);
346
347 // Sort list of matches, newest (biggest time) to oldest (lowest time).
348 std::sort(matches.begin(), matches.end(), MatchCompareFunc);
349
350 // Delete starting at element num_files_to_keep_.
351 vector<pair<FilePath, Time> >::const_iterator i;
352 for (i = matches.begin() + num_files_to_keep_; i < matches.end(); ++i) {
Alex Vakulenko75039d72014-03-25 12:36:28 -0700353 const base::FilePath& file = i->first;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700354 LOG(INFO) << "Deleting p2p file " << file.value();
355 if (unlink(file.value().c_str()) != 0) {
356 PLOG(ERROR) << "Error deleting p2p file " << file.value();
357 return false;
358 }
359 }
360
361 return true;
362}
363
364// Helper class for implementing LookupUrlForFile().
365class LookupData {
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700366 public:
367 explicit LookupData(P2PManager::LookupCallback callback)
David Zeuthen27a48bc2013-08-06 12:06:29 -0700368 : callback_(callback),
369 pid_(0),
370 stdout_fd_(-1),
371 stdout_channel_source_id_(0),
372 child_watch_source_id_(0),
373 timeout_source_id_(0),
374 reported_(false) {}
375
376 ~LookupData() {
377 if (child_watch_source_id_ != 0)
378 g_source_remove(child_watch_source_id_);
379 if (stdout_channel_source_id_ != 0)
380 g_source_remove(stdout_channel_source_id_);
381 if (timeout_source_id_ != 0)
382 g_source_remove(timeout_source_id_);
383 if (stdout_fd_ != -1)
384 close(stdout_fd_);
385 if (pid_ != 0)
386 kill(pid_, SIGTERM);
387 }
388
389 void InitiateLookup(gchar **argv, TimeDelta timeout) {
390 // NOTE: if we fail early (i.e. in this method), we need to schedule
391 // an idle to report the error. This is because we guarantee that
Alex Vakulenko072359c2014-07-18 11:41:07 -0700392 // the callback is always called from the GLib mainloop (this
David Zeuthen27a48bc2013-08-06 12:06:29 -0700393 // guarantee is useful for testing).
394
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700395 GError *error = nullptr;
396 if (!g_spawn_async_with_pipes(nullptr, // working_directory
David Zeuthen27a48bc2013-08-06 12:06:29 -0700397 argv,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700398 nullptr, // envp
David Zeuthen27a48bc2013-08-06 12:06:29 -0700399 static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
400 G_SPAWN_DO_NOT_REAP_CHILD),
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700401 nullptr, // child_setup
David Zeuthen27a48bc2013-08-06 12:06:29 -0700402 this,
403 &pid_,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700404 nullptr, // standard_input
David Zeuthen27a48bc2013-08-06 12:06:29 -0700405 &stdout_fd_,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700406 nullptr, // standard_error
David Zeuthen27a48bc2013-08-06 12:06:29 -0700407 &error)) {
408 LOG(ERROR) << "Error spawning p2p-client: "
409 << utils::GetAndFreeGError(&error);
410 ReportErrorAndDeleteInIdle();
411 return;
412 }
413
414 GIOChannel* io_channel = g_io_channel_unix_new(stdout_fd_);
415 stdout_channel_source_id_ = g_io_add_watch(
416 io_channel,
417 static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
418 OnIOChannelActivity, this);
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700419 CHECK_NE(stdout_channel_source_id_, 0u);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700420 g_io_channel_unref(io_channel);
421
422 child_watch_source_id_ = g_child_watch_add(pid_, OnChildWatchActivity,
423 this);
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700424 CHECK_NE(child_watch_source_id_, 0u);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700425
426 if (timeout.ToInternalValue() > 0) {
427 timeout_source_id_ = g_timeout_add(timeout.InMilliseconds(),
428 OnTimeout, this);
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700429 CHECK_NE(timeout_source_id_, 0u);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700430 }
431 }
432
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700433 private:
David Zeuthen27a48bc2013-08-06 12:06:29 -0700434 void ReportErrorAndDeleteInIdle() {
435 g_idle_add(static_cast<GSourceFunc>(OnIdleForReportErrorAndDelete), this);
436 }
437
438 static gboolean OnIdleForReportErrorAndDelete(gpointer user_data) {
439 LookupData *lookup_data = reinterpret_cast<LookupData*>(user_data);
440 lookup_data->ReportError();
441 delete lookup_data;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700442 return FALSE; // Remove source.
David Zeuthen27a48bc2013-08-06 12:06:29 -0700443 }
444
445 void IssueCallback(const string& url) {
446 if (!callback_.is_null())
447 callback_.Run(url);
448 }
449
450 void ReportError() {
451 if (reported_)
452 return;
453 IssueCallback("");
454 reported_ = true;
455 }
456
457 void ReportSuccess() {
458 if (reported_)
459 return;
460
461 string url = stdout_;
462 size_t newline_pos = url.find('\n');
463 if (newline_pos != string::npos)
464 url.resize(newline_pos);
465
466 // Since p2p-client(1) is constructing this URL itself strictly
467 // speaking there's no need to validate it... but, anyway, can't
468 // hurt.
469 if (url.compare(0, 7, "http://") == 0) {
470 IssueCallback(url);
471 } else {
472 LOG(ERROR) << "p2p URL '" << url << "' does not look right. Ignoring.";
473 ReportError();
474 }
475
476 reported_ = true;
477 }
478
479 static gboolean OnIOChannelActivity(GIOChannel *source,
480 GIOCondition condition,
481 gpointer user_data) {
482 LookupData *lookup_data = reinterpret_cast<LookupData*>(user_data);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700483 gchar* str = nullptr;
484 GError* error = nullptr;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700485 GIOStatus status = g_io_channel_read_line(source,
486 &str,
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700487 nullptr, // len
488 nullptr, // line_terminator
David Zeuthen27a48bc2013-08-06 12:06:29 -0700489 &error);
490 if (status != G_IO_STATUS_NORMAL) {
491 // Ignore EOF since we usually get that before SIGCHLD and we
492 // need to examine exit status there.
493 if (status != G_IO_STATUS_EOF) {
494 LOG(ERROR) << "Error reading a line from p2p-client: "
495 << utils::GetAndFreeGError(&error);
496 lookup_data->ReportError();
497 delete lookup_data;
498 }
499 } else {
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700500 if (str != nullptr) {
David Zeuthen27a48bc2013-08-06 12:06:29 -0700501 lookup_data->stdout_ += str;
502 g_free(str);
503 }
504 }
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700505 return TRUE; // Don't remove source.
David Zeuthen27a48bc2013-08-06 12:06:29 -0700506 }
507
508 static void OnChildWatchActivity(GPid pid,
509 gint status,
510 gpointer user_data) {
511 LookupData *lookup_data = reinterpret_cast<LookupData*>(user_data);
512
513 if (!WIFEXITED(status)) {
514 LOG(ERROR) << "Child didn't exit normally";
515 lookup_data->ReportError();
516 } else if (WEXITSTATUS(status) != 0) {
517 LOG(INFO) << "Child exited with non-zero exit code "
518 << WEXITSTATUS(status);
519 lookup_data->ReportError();
520 } else {
521 lookup_data->ReportSuccess();
522 }
523 delete lookup_data;
524 }
525
526 static gboolean OnTimeout(gpointer user_data) {
527 LookupData *lookup_data = reinterpret_cast<LookupData*>(user_data);
528 lookup_data->ReportError();
529 delete lookup_data;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700530 return TRUE; // Don't remove source.
David Zeuthen27a48bc2013-08-06 12:06:29 -0700531 }
532
533 P2PManager::LookupCallback callback_;
534 GPid pid_;
535 gint stdout_fd_;
536 guint stdout_channel_source_id_;
537 guint child_watch_source_id_;
538 guint timeout_source_id_;
539 string stdout_;
540 bool reported_;
541};
542
543void P2PManagerImpl::LookupUrlForFile(const string& file_id,
544 size_t minimum_size,
545 TimeDelta max_time_to_wait,
546 LookupCallback callback) {
547 LookupData *lookup_data = new LookupData(callback);
548 string file_id_with_ext = file_id + "." + file_extension_;
549 vector<string> args = configuration_->GetP2PClientArgs(file_id_with_ext,
550 minimum_size);
551 gchar **argv = utils::StringVectorToGStrv(args);
552 lookup_data->InitiateLookup(argv, max_time_to_wait);
553 g_strfreev(argv);
554}
555
556bool P2PManagerImpl::FileShare(const string& file_id,
557 size_t expected_size) {
558 // Check if file already exist.
Alex Vakulenko75039d72014-03-25 12:36:28 -0700559 base::FilePath path = FileGetPath(file_id);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700560 if (!path.empty()) {
561 // File exists - double check its expected size though.
562 ssize_t file_expected_size = FileGetExpectedSize(file_id);
563 if (file_expected_size == -1 ||
564 static_cast<size_t>(file_expected_size) != expected_size) {
565 LOG(ERROR) << "Existing p2p file " << path.value()
566 << " with expected_size=" << file_expected_size
567 << " does not match the passed in"
568 << " expected_size=" << expected_size;
569 return false;
570 }
571 return true;
572 }
573
574 // Before creating the file, bail if statvfs(3) indicates that at
575 // least twice the size is not available in P2P_DIR.
576 struct statvfs statvfsbuf;
Alex Vakulenko75039d72014-03-25 12:36:28 -0700577 base::FilePath p2p_dir = configuration_->GetP2PDir();
David Zeuthen27a48bc2013-08-06 12:06:29 -0700578 if (statvfs(p2p_dir.value().c_str(), &statvfsbuf) != 0) {
579 PLOG(ERROR) << "Error calling statvfs() for dir " << p2p_dir.value();
580 return false;
581 }
582 size_t free_bytes =
583 static_cast<size_t>(statvfsbuf.f_bsize) * statvfsbuf.f_bavail;
584 if (free_bytes < 2 * expected_size) {
585 // This can easily happen and is worth reporting.
586 LOG(INFO) << "Refusing to allocate p2p file of " << expected_size
587 << " bytes since the directory " << p2p_dir.value()
588 << " only has " << free_bytes
589 << " bytes available and this is less than twice the"
590 << " requested size.";
591 return false;
592 }
593
594 // Okie-dokey looks like enough space is available - create the file.
595 path = GetPath(file_id, kNonVisible);
596 int fd = open(path.value().c_str(), O_CREAT | O_RDWR, 0644);
597 if (fd == -1) {
598 PLOG(ERROR) << "Error creating file with path " << path.value();
599 return false;
600 }
601 ScopedFdCloser fd_closer(&fd);
602
603 // If the final size is known, allocate the file (e.g. reserve disk
604 // space) and set the user.cros-p2p-filesize xattr.
605 if (expected_size != 0) {
606 if (fallocate(fd,
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700607 FALLOC_FL_KEEP_SIZE, // Keep file size as 0.
David Zeuthen27a48bc2013-08-06 12:06:29 -0700608 0,
609 expected_size) != 0) {
David Zeuthen910ec5b2013-09-26 12:10:58 -0700610 if (errno == ENOSYS || errno == EOPNOTSUPP) {
611 // If the filesystem doesn't support the fallocate, keep
612 // going. This is helpful when running unit tests on build
613 // machines with ancient filesystems and/or OSes.
614 PLOG(WARNING) << "Ignoring fallocate(2) failure";
615 } else {
616 // ENOSPC can happen (funky race though, cf. the statvfs() check
617 // above), handle it gracefully, e.g. use logging level INFO.
618 PLOG(INFO) << "Error allocating " << expected_size
619 << " bytes for file " << path.value();
620 if (unlink(path.value().c_str()) != 0) {
621 PLOG(ERROR) << "Error deleting file with path " << path.value();
622 }
623 return false;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700624 }
David Zeuthen27a48bc2013-08-06 12:06:29 -0700625 }
626
Alex Vakulenko75039d72014-03-25 12:36:28 -0700627 string decimal_size = base::StringPrintf("%zu", expected_size);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700628 if (fsetxattr(fd, kCrosP2PFileSizeXAttrName,
629 decimal_size.c_str(), decimal_size.size(), 0) != 0) {
630 PLOG(ERROR) << "Error setting xattr " << path.value();
631 return false;
632 }
633 }
634
635 return true;
636}
637
638FilePath P2PManagerImpl::FileGetPath(const string& file_id) {
639 struct stat statbuf;
Alex Vakulenko75039d72014-03-25 12:36:28 -0700640 base::FilePath path;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700641
642 path = GetPath(file_id, kVisible);
643 if (stat(path.value().c_str(), &statbuf) == 0) {
644 return path;
645 }
646
647 path = GetPath(file_id, kNonVisible);
648 if (stat(path.value().c_str(), &statbuf) == 0) {
649 return path;
650 }
651
652 path.clear();
653 return path;
654}
655
656bool P2PManagerImpl::FileGetVisible(const string& file_id,
657 bool *out_result) {
Alex Vakulenko75039d72014-03-25 12:36:28 -0700658 base::FilePath path = FileGetPath(file_id);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700659 if (path.empty()) {
660 LOG(ERROR) << "No file for id " << file_id;
661 return false;
662 }
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700663 if (out_result != nullptr)
David Zeuthen27a48bc2013-08-06 12:06:29 -0700664 *out_result = path.MatchesExtension(kP2PExtension);
665 return true;
666}
667
668bool P2PManagerImpl::FileMakeVisible(const string& file_id) {
Alex Vakulenko75039d72014-03-25 12:36:28 -0700669 base::FilePath path = FileGetPath(file_id);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700670 if (path.empty()) {
671 LOG(ERROR) << "No file for id " << file_id;
672 return false;
673 }
674
675 // Already visible?
676 if (path.MatchesExtension(kP2PExtension))
677 return true;
678
679 LOG_ASSERT(path.MatchesExtension(kTmpExtension));
Alex Vakulenko75039d72014-03-25 12:36:28 -0700680 base::FilePath new_path = path.RemoveExtension();
David Zeuthen27a48bc2013-08-06 12:06:29 -0700681 LOG_ASSERT(new_path.MatchesExtension(kP2PExtension));
682 if (rename(path.value().c_str(), new_path.value().c_str()) != 0) {
683 PLOG(ERROR) << "Error renaming " << path.value()
684 << " to " << new_path.value();
685 return false;
686 }
687
688 return true;
689}
690
691ssize_t P2PManagerImpl::FileGetSize(const string& file_id) {
Alex Vakulenko75039d72014-03-25 12:36:28 -0700692 base::FilePath path = FileGetPath(file_id);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700693 if (path.empty())
694 return -1;
695
696 struct stat statbuf;
697 if (stat(path.value().c_str(), &statbuf) != 0) {
698 PLOG(ERROR) << "Error getting file status for " << path.value();
699 return -1;
700 }
701
702 return statbuf.st_size;
703}
704
705ssize_t P2PManagerImpl::FileGetExpectedSize(const string& file_id) {
Alex Vakulenko75039d72014-03-25 12:36:28 -0700706 base::FilePath path = FileGetPath(file_id);
David Zeuthen27a48bc2013-08-06 12:06:29 -0700707 if (path.empty())
708 return -1;
709
710 char ea_value[64] = { 0 };
711 ssize_t ea_size;
712 ea_size = getxattr(path.value().c_str(), kCrosP2PFileSizeXAttrName,
713 &ea_value, sizeof(ea_value) - 1);
714 if (ea_size == -1) {
715 PLOG(ERROR) << "Error calling getxattr() on file " << path.value();
716 return -1;
717 }
718
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700719 char* endp = nullptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700720 long long int val = strtoll(ea_value, &endp, 0); // NOLINT(runtime/int)
David Zeuthen27a48bc2013-08-06 12:06:29 -0700721 if (*endp != '\0') {
722 LOG(ERROR) << "Error parsing the value '" << ea_value
723 << "' of the xattr " << kCrosP2PFileSizeXAttrName
724 << " as an integer";
725 return -1;
726 }
727
728 return val;
729}
730
731int P2PManagerImpl::CountSharedFiles() {
732 GDir* dir;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700733 GError* error = nullptr;
David Zeuthen27a48bc2013-08-06 12:06:29 -0700734 const char* name;
735 int num_files = 0;
736
Alex Vakulenko75039d72014-03-25 12:36:28 -0700737 base::FilePath p2p_dir = configuration_->GetP2PDir();
David Zeuthen27a48bc2013-08-06 12:06:29 -0700738 dir = g_dir_open(p2p_dir.value().c_str(), 0, &error);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700739 if (dir == nullptr) {
David Zeuthen27a48bc2013-08-06 12:06:29 -0700740 LOG(ERROR) << "Error opening directory " << p2p_dir.value() << ": "
741 << utils::GetAndFreeGError(&error);
742 return -1;
743 }
744
745 string ext_visible = GetExt(kVisible);
746 string ext_non_visible = GetExt(kNonVisible);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700747 while ((name = g_dir_read_name(dir)) != nullptr) {
David Zeuthen27a48bc2013-08-06 12:06:29 -0700748 if (g_str_has_suffix(name, ext_visible.c_str()) ||
749 g_str_has_suffix(name, ext_non_visible.c_str())) {
750 num_files += 1;
751 }
752 }
753 g_dir_close(dir);
754
755 return num_files;
756}
757
758P2PManager* P2PManager::Construct(Configuration *configuration,
759 PrefsInterface *prefs,
760 const string& file_extension,
761 const int num_files_to_keep) {
762 return new P2PManagerImpl(configuration,
763 prefs,
764 file_extension,
765 num_files_to_keep);
766}
767
768} // namespace chromeos_update_engine