| // 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 <windows.h> |
| |
| #include "base/files/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 base::FilePath& temp_parent_dir, |
| base::FilePath* base_dir) { |
| DCHECK(base_dir); |
| |
| if (base::PathExists(temp_parent_dir)) { |
| // Empty base_dir means that we didn't create any extra directories. |
| base_dir->clear(); |
| } else { |
| base::FilePath parent_dir(temp_parent_dir); |
| do { |
| *base_dir = parent_dir; |
| parent_dir = parent_dir.DirName(); |
| } while (parent_dir != *base_dir && !base::PathExists(parent_dir)); |
| LOG_IF(WARNING, !base::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 base::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; |
| } |
| |
| base::FilePath temp_dir(parent_dir.Append(temp_name)); |
| base::FilePath base_dir; |
| GetTopDirToCreate(parent_dir, &base_dir); |
| |
| if (base::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; |
| } |
| |
| base::FilePath next_dir(path().DirName()); |
| bool schedule_deletes = false; |
| |
| // First try to recursively delete the leaf directory managed by our |
| // base::ScopedTempDir. |
| if (!base::DeleteFile(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())) |
| 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); |
| } |
| 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 |