1 // Copyright (c) 2011 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 #include "chrome/installer/util/self_cleaning_temp_dir.h" 6 7 #include <windows.h> 8 9 #include "base/file_util.h" 10 #include "base/logging.h" 11 #include "chrome/installer/util/delete_after_reboot_helper.h" 12 13 namespace installer { 14 15 // Populates |base_dir| with the topmost directory in the hierarchy of 16 // |temp_parent_dir| that does not exist. If |temp_parent_dir| exists, 17 // |base_dir| is cleared. 18 // static 19 void SelfCleaningTempDir::GetTopDirToCreate( 20 const base::FilePath& temp_parent_dir, 21 base::FilePath* base_dir) { 22 DCHECK(base_dir); 23 24 if (base::PathExists(temp_parent_dir)) { 25 // Empty base_dir means that we didn't create any extra directories. 26 base_dir->clear(); 27 } else { 28 base::FilePath parent_dir(temp_parent_dir); 29 do { 30 *base_dir = parent_dir; 31 parent_dir = parent_dir.DirName(); 32 } while (parent_dir != *base_dir && !base::PathExists(parent_dir)); 33 LOG_IF(WARNING, !base::DirectoryExists(parent_dir)) 34 << "A non-directory is at the base of the path leading to a desired " 35 "temp directory location: " << parent_dir.value(); 36 } 37 } 38 39 SelfCleaningTempDir::SelfCleaningTempDir() { 40 } 41 42 SelfCleaningTempDir::~SelfCleaningTempDir() { 43 if (!path().empty() && !Delete()) 44 LOG(WARNING) << "Failed to clean temp dir in dtor " << path().value(); 45 } 46 47 bool SelfCleaningTempDir::Initialize(const base::FilePath& parent_dir, 48 const StringType& temp_name) { 49 DCHECK(parent_dir.IsAbsolute()); 50 DCHECK(!temp_name.empty()); 51 52 if (!path().empty()) { 53 LOG(DFATAL) << "Attempting to re-initialize a SelfSelfCleaningTempDir."; 54 return false; 55 } 56 57 base::FilePath temp_dir(parent_dir.Append(temp_name)); 58 base::FilePath base_dir; 59 GetTopDirToCreate(parent_dir, &base_dir); 60 61 if (base::CreateDirectory(temp_dir)) { 62 base_dir_ = base_dir; 63 temp_dir_ = temp_dir; 64 return true; 65 } 66 67 return false; 68 } 69 70 bool SelfCleaningTempDir::Delete() { 71 if (path().empty()) { 72 LOG(DFATAL) << "Attempting to Delete an uninitialized SelfCleaningTempDir."; 73 return false; 74 } 75 76 base::FilePath next_dir(path().DirName()); 77 bool schedule_deletes = false; 78 79 // First try to recursively delete the leaf directory managed by our 80 // base::ScopedTempDir. 81 if (!base::DeleteFile(path(), true)) { 82 // That failed, so schedule the temp dir and its contents for deletion after 83 // reboot. 84 LOG(WARNING) << "Failed to delete temporary directory " << path().value() 85 << ". Scheduling for deletion at reboot."; 86 schedule_deletes = true; 87 if (!ScheduleDirectoryForDeletion(path())) 88 return false; // Entirely unexpected failure (Schedule logs the reason). 89 } 90 91 // Now delete or schedule all empty directories up to and including our 92 // base_dir_. Any that can't be deleted are scheduled for deletion at reboot. 93 // This is safe since they'll only be deleted in that case if they're empty. 94 if (!base_dir_.empty()) { 95 do { 96 if (!schedule_deletes && !RemoveDirectory(next_dir.value().c_str())) { 97 PLOG_IF(WARNING, GetLastError() != ERROR_DIR_NOT_EMPTY) 98 << "Error removing directory " << next_dir.value().c_str(); 99 schedule_deletes = true; 100 } 101 if (schedule_deletes) { 102 // Ignore the return code. If we fail to schedule, go ahead and add the 103 // other parent directories anyway. 104 ScheduleFileSystemEntityForDeletion(next_dir); 105 } 106 if (next_dir == base_dir_) 107 break; // We just processed the topmost directory we created. 108 next_dir = next_dir.DirName(); 109 } while (true); 110 } 111 112 base_dir_.clear(); 113 temp_dir_.clear(); 114 115 return true; 116 } 117 118 } // namespace installer 119