GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/catalog_diff_tool_impl.h Lines: 92 110 83.6 %
Date: 2019-02-03 02:48:13 Branches: 56 93 60.2 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 */
4
5
#ifndef CVMFS_CATALOG_DIFF_TOOL_IMPL_H_
6
#define CVMFS_CATALOG_DIFF_TOOL_IMPL_H_
7
8
#include <algorithm>
9
#include <string>
10
11
#include "catalog.h"
12
#include "download.h"
13
#include "hash.h"
14
#include "logging.h"
15
#include "util/posix.h"
16
17
const uint64_t kLastInode = uint64_t(-1);
18
19
8
inline void AppendFirstEntry(catalog::DirectoryEntryList* entry_list) {
20
8
  catalog::DirectoryEntry empty_entry;
21
8
  entry_list->push_back(empty_entry);
22
8
}
23
24
8
inline void AppendLastEntry(catalog::DirectoryEntryList* entry_list) {
25
8
  assert(!entry_list->empty());
26
8
  catalog::DirectoryEntry last_entry;
27
8
  last_entry.set_inode(kLastInode);
28
8
  entry_list->push_back(last_entry);
29
8
}
30
31
48
inline bool IsSmaller(const catalog::DirectoryEntry& a,
32
                      const catalog::DirectoryEntry& b) {
33
48
  bool a_is_first = (a.inode() == catalog::DirectoryEntryBase::kInvalidInode);
34
48
  bool a_is_last = (a.inode() == kLastInode);
35
48
  bool b_is_first = (b.inode() == catalog::DirectoryEntryBase::kInvalidInode);
36
48
  bool b_is_last = (b.inode() == kLastInode);
37
38

48
  if (a_is_last || b_is_first) return false;
39
15
  if (a_is_first) return !b_is_first;
40
15
  if (b_is_last) return !a_is_last;
41
12
  return a.name() < b.name();
42
}
43
44
template <typename RoCatalogMgr>
45
1
bool CatalogDiffTool<RoCatalogMgr>::Init() {
46
1
  if (needs_setup_) {
47
    // Create a temp directory
48
1
    old_raii_temp_dir_ = RaiiTempDir::Create(temp_dir_prefix_);
49
1
    new_raii_temp_dir_ = RaiiTempDir::Create(temp_dir_prefix_);
50
51
    // Old catalog from release manager machine (before lease)
52
1
    old_catalog_mgr_ =
53
        OpenCatalogManager(repo_path_, old_raii_temp_dir_->dir(),
54
                           old_root_hash_, download_manager_, &stats_old_);
55
56
    // New catalog from release manager machine (before lease)
57
1
    new_catalog_mgr_ =
58
        OpenCatalogManager(repo_path_, new_raii_temp_dir_->dir(),
59
                           new_root_hash_, download_manager_, &stats_new_);
60
61
1
    if (!old_catalog_mgr_.IsValid()) {
62
      LogCvmfs(kLogCvmfs, kLogStderr, "Could not open old catalog");
63
      return false;
64
    }
65
66
1
    if (!new_catalog_mgr_.IsValid()) {
67
      LogCvmfs(kLogCvmfs, kLogStderr, "Could not open new catalog");
68
      return false;
69
    }
70
  }
71
72
1
  return true;
73
}
74
75
template <typename RoCatalogMgr>
76
1
bool CatalogDiffTool<RoCatalogMgr>::Run(const PathString& path) {
77
1
  DiffRec(path);
78
79
1
  return true;
80
}
81
82
template <typename RoCatalogMgr>
83
2
RoCatalogMgr* CatalogDiffTool<RoCatalogMgr>::OpenCatalogManager(
84
    const std::string& repo_path, const std::string& temp_dir,
85
    const shash::Any& root_hash, download::DownloadManager* download_manager,
86
    perf::Statistics* stats) {
87
  RoCatalogMgr* mgr = new RoCatalogMgr(root_hash, repo_path, temp_dir,
88
2
                                       download_manager, stats, true);
89
2
  mgr->Init();
90
91
2
  return mgr;
92
}
93
94
template <typename RoCatalogMgr>
95
4
void CatalogDiffTool<RoCatalogMgr>::DiffRec(const PathString& path) {
96
4
  catalog::DirectoryEntryList old_listing;
97
4
  AppendFirstEntry(&old_listing);
98
4
  old_catalog_mgr_->Listing(path, &old_listing);
99
4
  sort(old_listing.begin(), old_listing.end(), IsSmaller);
100
4
  AppendLastEntry(&old_listing);
101
102
4
  catalog::DirectoryEntryList new_listing;
103
4
  AppendFirstEntry(&new_listing);
104
4
  new_catalog_mgr_->Listing(path, &new_listing);
105
4
  sort(new_listing.begin(), new_listing.end(), IsSmaller);
106
4
  AppendLastEntry(&new_listing);
107
108
4
  unsigned i_from = 0, size_from = old_listing.size();
109
4
  unsigned i_to = 0, size_to = new_listing.size();
110





4
  while ((i_from < size_from) || (i_to < size_to)) {
111
15
    catalog::DirectoryEntry old_entry = old_listing[i_from];
112
15
    catalog::DirectoryEntry new_entry = new_listing[i_to];
113
114
15
    if (old_entry.linkcount() == 0) {
115
      LogCvmfs(kLogCvmfs, kLogStderr,
116
                "CatalogDiffTool - Entry %s in old catalog has linkcount 0. "
117
                "Aborting.",
118
                old_entry.name().c_str());
119
      abort();
120
    }
121
15
    if (new_entry.linkcount() == 0) {
122
      LogCvmfs(kLogCvmfs, kLogStderr,
123
                "CatalogDiffTool - Entry %s in new catalog has linkcount 0. "
124
                "Aborting.",
125
                new_entry.name().c_str());
126
      abort();
127
    }
128
129
    // Skip .cvmfs hidden directory
130
15
    while (old_entry.IsHidden()) old_entry = old_listing[++i_from];
131
15
    while (new_entry.IsHidden()) new_entry = new_listing[++i_to];
132
133
15
    PathString old_path(path);
134
15
    old_path.Append("/", 1);
135
15
    old_path.Append(old_entry.name().GetChars(), old_entry.name().GetLength());
136
15
    PathString new_path(path);
137
15
    new_path.Append("/", 1);
138
15
    new_path.Append(new_entry.name().GetChars(), new_entry.name().GetLength());
139
140
15
    XattrList xattrs;
141
15
    if (new_entry.HasXattrs()) {
142
      new_catalog_mgr_->LookupXattrs(new_path, &xattrs);
143
    }
144
145
15
    if (IsSmaller(new_entry, old_entry)) {
146
2
      i_to++;
147
2
      FileChunkList chunks;
148
2
      if (new_entry.IsChunkedFile()) {
149
        new_catalog_mgr_->ListFileChunks(new_path, new_entry.hash_algorithm(),
150
                                         &chunks);
151
      }
152
2
      ReportAddition(new_path, new_entry, xattrs, chunks);
153
2
      if (new_entry.IsDirectory()) {
154
1
        DiffRec(new_path);
155
      }
156
2
      continue;
157
13
    } else if (IsSmaller(old_entry, new_entry)) {
158
2
      i_from++;
159

2
      if (old_entry.IsDirectory() && !old_entry.IsNestedCatalogMountpoint()) {
160
1
        DiffRec(old_path);
161
      }
162
2
      ReportRemoval(old_path, old_entry);
163
2
      continue;
164
    }
165
166
11
    assert(old_path == new_path);
167
11
    i_from++;
168
11
    i_to++;
169
170
    catalog::DirectoryEntryBase::Differences diff =
171
11
        old_entry.CompareTo(new_entry);
172

11
    if ((diff == catalog::DirectoryEntryBase::Difference::kIdentical) &&
173
        old_entry.IsNestedCatalogMountpoint()) {
174
      // Early recursion stop if nested catalogs are identical
175
      shash::Any id_nested_from, id_nested_to;
176
      id_nested_from = old_catalog_mgr_->GetNestedCatalogHash(old_path);
177
      id_nested_to = new_catalog_mgr_->GetNestedCatalogHash(new_path);
178
      assert(!id_nested_from.IsNull() && !id_nested_to.IsNull());
179
      if (id_nested_from == id_nested_to) continue;
180
    }
181
182
11
    if (old_entry.CompareTo(new_entry) > 0) {
183
1
      FileChunkList chunks;
184
1
      if (new_entry.IsChunkedFile()) {
185
        new_catalog_mgr_->ListFileChunks(new_path, new_entry.hash_algorithm(),
186
                                         &chunks);
187
      }
188
1
      ReportModification(old_path, old_entry, new_entry, xattrs, chunks);
189
    }
190

11
    if (!old_entry.IsDirectory() || !new_entry.IsDirectory()) {
191
10
      if (old_entry.IsDirectory()) {
192
        DiffRec(old_path);
193
10
      } else if (new_entry.IsDirectory()) {
194
        DiffRec(new_path);
195
      }
196
10
      continue;
197
    }
198
199
    // Recursion
200
1
    DiffRec(old_path);
201
  }
202
4
}
203
204
#endif  // CVMFS_CATALOG_DIFF_TOOL_IMPL_H_