move_tree_work_item.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/move_tree_work_item.h"
6
7#include <shlwapi.h>
8
9#include "base/file_util.h"
10#include "base/logging.h"
11#include "chrome/installer/util/duplicate_tree_detector.h"
12#include "chrome/installer/util/logging_installer.h"
13
14MoveTreeWorkItem::~MoveTreeWorkItem() {
15}
16
17MoveTreeWorkItem::MoveTreeWorkItem(const base::FilePath& source_path,
18                                   const base::FilePath& dest_path,
19                                   const base::FilePath& temp_dir,
20                                   MoveTreeOption duplicate_option)
21    : source_path_(source_path),
22      dest_path_(dest_path),
23      temp_dir_(temp_dir),
24      duplicate_option_(duplicate_option),
25      moved_to_dest_path_(false),
26      moved_to_backup_(false),
27      source_moved_to_backup_(false) {
28}
29
30bool MoveTreeWorkItem::Do() {
31  if (!base::PathExists(source_path_)) {
32    LOG(ERROR) << source_path_.value() << " does not exist";
33    return false;
34  }
35
36  // If dest_path_ exists, we can do one of two things:
37  // 1) If the contents of src_path_are already fully contained in dest_path_
38  //    then do nothing and return success. Fully contained means the full
39  //    file structure with identical files is contained in dest_path_. For
40  //    Chrome, if dest_path_ exists, this is expected to be the common case.
41  // 2) If the contents of src_path_ are NOT fully contained in dest_path_, we
42  //    attempt to backup dest_path_ and replace it with src_path_. This will
43  //    fail if files in dest_path_ are in use.
44  if (base::PathExists(dest_path_)) {
45    // Generate a backup path that can keep the original files under dest_path_.
46    if (!backup_path_.CreateUniqueTempDirUnderPath(temp_dir_)) {
47      PLOG(ERROR) << "Failed to get backup path in folder "
48                  << temp_dir_.value();
49      return false;
50    }
51    base::FilePath backup = backup_path_.path().Append(dest_path_.BaseName());
52
53    if (duplicate_option_ == CHECK_DUPLICATES) {
54      if (installer::IsIdenticalFileHierarchy(source_path_, dest_path_)) {
55        // The files we are moving are already present in the destination path.
56        // We most likely don't need to do anything. As such, just move the
57        // source files to the temp folder as backup.
58        if (base::Move(source_path_, backup)) {
59          source_moved_to_backup_ = true;
60          VLOG(1) << "Moved source " << source_path_.value()
61                  << " to backup path " << backup.value();
62          return true;
63        } else {
64          // We failed to move the source tree to the backup path. This is odd
65          // but just fall through and attempt the regular behaviour as well.
66          LOG(ERROR) << "Failed to backup source " << source_path_.value()
67                     << " to backup path " << backup.value()
68                     << " for duplicate trees. Trying regular Move instead.";
69        }
70      } else {
71        VLOG(1) << "Source path " << source_path_.value()
72                << " differs from " << dest_path_.value()
73                << ", updating now.";
74      }
75    }
76
77    if (base::Move(dest_path_, backup)) {
78      moved_to_backup_ = true;
79      VLOG(1) << "Moved destination " << dest_path_.value()
80              << " to backup path " << backup.value();
81    } else {
82      PLOG(ERROR) << "failed moving " << dest_path_.value()
83                  << " to " << backup.value();
84      return false;
85    }
86  }
87
88  // Now move source to destination.
89  if (base::Move(source_path_, dest_path_)) {
90    moved_to_dest_path_ = true;
91    VLOG(1) << "Moved source " << source_path_.value()
92            << " to destination " << dest_path_.value();
93  } else {
94    PLOG(ERROR) << "failed move " << source_path_.value()
95                << " to " << dest_path_.value();
96    return false;
97  }
98
99  return true;
100}
101
102void MoveTreeWorkItem::Rollback() {
103  if (moved_to_dest_path_ && !base::Move(dest_path_, source_path_))
104    LOG(ERROR) << "Can not move " << dest_path_.value()
105               << " to " << source_path_.value();
106
107  base::FilePath backup = backup_path_.path().Append(dest_path_.BaseName());
108  if (moved_to_backup_ && !base::Move(backup, dest_path_)) {
109    LOG(ERROR) << "failed move " << backup.value()
110               << " to " << dest_path_.value();
111  }
112
113  if (source_moved_to_backup_ && !base::Move(backup, source_path_)) {
114    LOG(ERROR) << "Can not restore " << backup.value()
115               << " to " << source_path_.value();
116  }
117}
118