Home | History | Annotate | Download | only in util
      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