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