self_cleaning_temp_dir.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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/files/file_util.h"
10#include "base/logging.h"
11#include "chrome/installer/util/delete_after_reboot_helper.h"
12
13namespace 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
19void 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
39SelfCleaningTempDir::SelfCleaningTempDir() {
40}
41
42SelfCleaningTempDir::~SelfCleaningTempDir() {
43  if (!path().empty() && !Delete())
44    LOG(WARNING) << "Failed to clean temp dir in dtor " << path().value();
45}
46
47bool 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
70bool 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