summaryrefslogtreecommitdiffstats
path: root/chrome/installer/util/self_cleaning_temp_dir.cc
blob: a95a722946644735b467e8f1703ee2463b18459d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// 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/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