// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/installer/util/self_cleaning_temp_dir.h" #include #include "base/file_util.h" #include "base/logging.h" #include "chrome/installer/util/delete_after_reboot_helper.h" namespace installer { // Populates |base_dir| with the topmost directory in the hierarchy of // |temp_parent_dir| that does not exist. If |temp_parent_dir| exists, // |base_dir| is cleared. // static void SelfCleaningTempDir::GetTopDirToCreate(const FilePath& temp_parent_dir, FilePath* base_dir) { DCHECK(base_dir); if (file_util::PathExists(temp_parent_dir)) { // Empty base_dir means that we didn't create any extra directories. base_dir->clear(); } else { FilePath parent_dir(temp_parent_dir); do { *base_dir = parent_dir; parent_dir = parent_dir.DirName(); } while (parent_dir != *base_dir && !file_util::PathExists(parent_dir)); LOG_IF(WARNING, !file_util::DirectoryExists(parent_dir)) << "A non-directory is at the base of the path leading to a desired " "temp directory location: " << parent_dir.value(); } } SelfCleaningTempDir::SelfCleaningTempDir() { } SelfCleaningTempDir::~SelfCleaningTempDir() { if (!path().empty() && !Delete()) LOG(WARNING) << "Failed to clean temp dir in dtor " << path().value(); } bool SelfCleaningTempDir::Initialize(const FilePath& parent_dir, const StringType& temp_name) { DCHECK(parent_dir.IsAbsolute()); DCHECK(!temp_name.empty()); if (!path().empty()) { LOG(DFATAL) << "Attempting to re-initialize a SelfSelfCleaningTempDir."; return false; } FilePath temp_dir(parent_dir.Append(temp_name)); FilePath base_dir; GetTopDirToCreate(parent_dir, &base_dir); if (file_util::CreateDirectory(temp_dir)) { base_dir_ = base_dir; temp_dir_ = temp_dir; return true; } return false; } bool SelfCleaningTempDir::Delete() { if (path().empty()) { LOG(DFATAL) << "Attempting to Delete an uninitialized SelfCleaningTempDir."; return false; } FilePath next_dir(path().DirName()); bool schedule_deletes = false; // First try to recursively delete the leaf directory managed by our // ScopedTempDir. if (!file_util::Delete(path(), true)) { // That failed, so schedule the temp dir and its contents for deletion after // reboot. LOG(WARNING) << "Failed to delete temporary directory " << path().value() << ". Scheduling for deletion at reboot."; schedule_deletes = true; if (!ScheduleDirectoryForDeletion(path().value().c_str())) return false; // Entirely unexpected failure (Schedule logs the reason). } // Now delete or schedule all empty directories up to and including our // base_dir_. Any that can't be deleted are scheduled for deletion at reboot. // This is safe since they'll only be deleted in that case if they're empty. if (!base_dir_.empty()) { do { if (!schedule_deletes && !RemoveDirectory(next_dir.value().c_str())) { PLOG_IF(WARNING, GetLastError() != ERROR_DIR_NOT_EMPTY) << "Error removing directory " << next_dir.value().c_str(); schedule_deletes = true; } if (schedule_deletes) { // Ignore the return code. If we fail to schedule, go ahead and add the // other parent directories anyway. ScheduleFileSystemEntityForDeletion(next_dir.value().c_str()); } if (next_dir == base_dir_) break; // We just processed the topmost directory we created. next_dir = next_dir.DirName(); } while (true); } base_dir_.clear(); temp_dir_.clear(); return true; } } // namespace installer