CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
fs_traversal.cc
Go to the documentation of this file.
1 
6 
7 #include <errno.h>
8 #include <pthread.h>
9 #include <stdio.h>
10 
11 #include <fstream>
12 #include <map>
13 #include <vector>
14 
15 #include "libcvmfs.h"
19 #include "shrinkwrap/spec_tree.h"
20 #include "statistics.h"
21 #include "util/atomic.h"
22 #include "util/concurrency.h"
23 #include "util/logging.h"
24 #include "util/platform.h"
25 #include "util/posix.h"
26 #include "util/smalloc.h"
27 #include "util/string.h"
28 
29 using namespace std; // NOLINT
30 
31 // Taken from fsck
32 enum Errors {
33  kErrorOk = 0,
39 };
40 
41 int strcmp_list(const void *a, const void *b) {
42  char const **char_a = (char const **)a;
43  char const **char_b = (char const **)b;
44 
45  return strcmp(*char_a, *char_b);
46 }
47 
48 namespace shrinkwrap {
49 
50 namespace {
51 
52 // No destructor is written to prevent double free and
53 // corruption of string pointers. After the copy written
54 // to the pipe, the new copy falls out of scope and
55 // is destroyed. If there is a destructor that frees the
56 // strings they get deleted after writing to the pipe.
57 class FileCopy {
58  public:
59  FileCopy() : src(NULL), dest(NULL) { }
60 
61  FileCopy(char *src, char *dest) : src(strdup(src)), dest(strdup(dest)) { }
62 
63  bool IsTerminateJob() const { return ((src == NULL) && (dest == NULL)); }
64 
65  char *src;
66  char *dest;
67 };
68 
69 class RecDir {
70  public:
71  RecDir() : dir(NULL), recursive(false) { }
72 
73  RecDir(const char *dir, bool recursive)
74  : dir(strdup(dir)), recursive(recursive) { }
75 
76  ~RecDir() { free(dir); }
77 
78  char *dir;
79  bool recursive;
80 };
81 
82 unsigned num_parallel_ = 0;
83 bool recursive = true;
84 uint64_t stat_update_period_ = 0; // Off for testing
85 int pipe_chunks[2];
86 // required for concurrent reading
87 pthread_mutex_t lock_pipe = PTHREAD_MUTEX_INITIALIZER;
89 
90 vector<RecDir *> dirs_;
91 
93 
94 } // namespace
95 
96 struct fs_traversal *FindInterface(const char *type) {
97  if (!strcmp(type, "posix")) {
98  return posix_get_interface();
99  } else if (!strcmp(type, "cvmfs")) {
100  return libcvmfs_get_interface();
101  }
102  LogCvmfs(kLogCvmfs, kLogStderr, "Unknown File System Interface : %s", type);
103  return NULL;
104 }
105 
106 bool cvmfs_attr_cmp(struct cvmfs_attr *src, struct cvmfs_attr *dest,
107  struct fs_traversal *dest_fs) {
108  if (!src) {
109  return false;
110  }
111  if (!dest) {
112  return false;
113  }
114  if (src->version != dest->version) {
115  return false;
116  }
117  if (src->size != dest->size) {
118  return false;
119  }
120 
121  // Actual contents of stat, mapped from DirectoryEntry
122  if ((!S_ISLNK(src->st_mode) && src->st_mode != dest->st_mode)
123  || ((S_IFMT & src->st_mode) != (S_IFMT & dest->st_mode))) {
124  return false;
125  }
126 
127  if (!S_ISLNK(src->st_mode) && src->st_uid != dest->st_uid) {
128  return false;
129  }
130  if (!S_ISLNK(src->st_mode) && src->st_gid != dest->st_gid) {
131  return false;
132  }
133 
134 
135  // CVMFS related content
136  if (S_ISREG(src->st_mode) && src->cvm_checksum) {
137  if (dest->cvm_checksum != NULL) {
138  if (strcmp(src->cvm_checksum, dest->cvm_checksum)) {
139  return false;
140  }
141  } else if (!dest_fs->is_hash_consistent(dest_fs->context_, src)) {
142  return false;
143  }
144  }
145 
146  if (S_ISLNK(src->st_mode)) {
147  if (strcmp(src->cvm_symlink, dest->cvm_symlink)) {
148  return false;
149  }
150  }
151 
152  if (strcmp(src->cvm_name, dest->cvm_name)) {
153  return false;
154  }
155 
156  // TODO(nhazekam): Not comparing Xattrs yet
157  // void * cvm_xattrs;
158  return true;
159 }
160 
161 bool copyFile(struct fs_traversal *src_fs,
162  const char *src_name,
163  struct fs_traversal *dest_fs,
164  const char *dest_name,
165  perf::Statistics *pstats) {
166  int retval = 0;
167  bool status = true;
168  size_t bytes_transferred = 0;
169 
170  // Get file handles from each respective filesystem
171  void *src = src_fs->get_handle(src_fs->context_, src_name);
172  void *dest = dest_fs->get_handle(dest_fs->context_, dest_name);
173 
174  retval = src_fs->do_fopen(src, fs_open_read);
175  if (retval != 0) {
176  LogCvmfs(kLogCvmfs, kLogStderr, "Failed open src : %s : %d : %s\n",
177  src_name, errno, strerror(errno));
178  status = false;
179  goto copy_fail;
180  }
181 
182  retval = dest_fs->do_fopen(dest, fs_open_write);
183  if (retval != 0) {
184  LogCvmfs(kLogCvmfs, kLogStderr, "Failed open dest : %s : %d : %s\n",
185  dest_name, errno, strerror(errno));
186  status = false;
187  goto copy_fail;
188  }
189 
190  while (status) {
191  char buffer[COPY_BUFFER_SIZE];
192 
193  size_t actual_read = 0;
194  retval = src_fs->do_fread(src, buffer, sizeof(buffer), &actual_read);
195  if (retval != 0) {
196  LogCvmfs(kLogCvmfs, kLogStderr, "Read failed : %d %s\n", errno,
197  strerror(errno));
198  status = false;
199  goto copy_fail;
200  }
201  bytes_transferred += actual_read;
202 
203  retval = dest_fs->do_fwrite(dest, buffer, actual_read);
204  if (retval != 0) {
205  LogCvmfs(kLogCvmfs, kLogStderr, "Write failed : %d %s\n", errno,
206  strerror(errno));
207  status = false;
208  goto copy_fail;
209  }
210 
211  if (actual_read < COPY_BUFFER_SIZE) {
212  break;
213  }
214  }
215 
216  pstats->Lookup(SHRINKWRAP_STAT_DATA_BYTES)->Xadd(bytes_transferred);
217 
218  retval = src_fs->do_fclose(src);
219  if (retval != 0) {
220  LogCvmfs(kLogCvmfs, kLogStderr, "Failed close src : %s : %d : %s\n",
221  src_name, errno, strerror(errno));
222  status = false;
223  goto copy_fail;
224  }
225 
226  retval = dest_fs->do_fclose(dest);
227  if (retval != 0) {
228  LogCvmfs(kLogCvmfs, kLogStderr, "Failed close dest : %s : %d : %s\n",
229  dest_name, errno, strerror(errno));
230  status = false;
231  goto copy_fail;
232  }
233 
234 copy_fail:
235  // This function will close the file if still open
236  // and free the handle.
237  src_fs->do_ffree(src);
238  dest_fs->do_ffree(dest);
239 
240  return status;
241 }
242 
243 char *get_full_path(const char *dir, const char *entry) {
244  size_t len = 2 + strlen(dir) + strlen(entry);
245  char *path = reinterpret_cast<char *>(smalloc(len));
246  snprintf(path, len, "%s/%s", dir, entry);
247  return path;
248 }
249 
250 bool updateStat(struct fs_traversal *fs,
251  const char *entry,
252  struct cvmfs_attr **st,
253  bool get_hash) {
254  cvmfs_attr_free(*st);
255  *st = NULL;
256  *st = cvmfs_attr_init();
257  int retval = fs->get_stat(fs->context_, entry, *st, get_hash);
258  if (retval != 0) {
259  return false;
260  }
261  return true;
262 }
263 
264 bool getNext(struct fs_traversal *fs,
265  const char *dir,
266  char **dir_list,
267  char **entry,
268  ssize_t *iter,
269  struct cvmfs_attr **st,
270  bool get_hash,
271  bool is_src,
272  perf::Statistics *pstats) {
273  size_t location = 0;
274  if (iter) {
275  location = (*iter) + 1;
276  *iter = location;
277  }
278 
279  free(*entry);
280  *entry = NULL;
281 
282  if (dir_list && dir_list[location]) {
283  *entry = get_full_path(dir, dir_list[location]);
284  if (entry && is_src && !spec_tree_->IsMatching(string(*entry))) {
285  return getNext(fs, dir, dir_list, entry, iter, st, get_hash, is_src,
286  pstats);
287  }
288  if (entry && !updateStat(fs, *entry, st, get_hash)) {
289  return getNext(fs, dir, dir_list, entry, iter, st, get_hash, is_src,
290  pstats);
291  }
292  } else {
293  return false;
294  }
295  if (is_src) {
297  switch ((*st)->st_mode & S_IFMT) {
298  case S_IFREG:
300  pstats->Lookup(SHRINKWRAP_STAT_COUNT_BYTE)->Xadd((*st)->st_size);
301  break;
302  case S_IFDIR:
304  break;
305  case S_IFLNK:
307  break;
308  }
309  } else {
311  }
312  return true;
313 }
314 
315 void list_src_dir(struct fs_traversal *src,
316  const char *dir,
317  char ***buf,
318  size_t *len) {
319  int retval = spec_tree_->ListDir(dir, buf, len);
320 
321  if (retval == SPEC_READ_FS) {
322  src->list_dir(src->context_, dir, buf, len);
323  qsort(*buf, *len, sizeof(char *), strcmp_list);
324  }
325 }
326 
327 bool handle_file(struct fs_traversal *src,
328  struct cvmfs_attr *src_st,
329  struct fs_traversal *dest,
330  struct cvmfs_attr *dest_st,
331  const char *entry,
332  perf::Statistics *pstats) {
333  bool result = true;
334  // They don't point to the same data, link new data
335  char *dest_data = dest->get_identifier(dest->context_, src_st);
336 
337  // Touch is atomic, if it fails something else will write file
338  if (!dest->touch(dest->context_, src_st)) {
339  char *src_ident = src->get_identifier(src->context_, src_st);
340  if (num_parallel_) {
341  FileCopy next_copy(src_ident, dest_data);
342 
343  WritePipe(pipe_chunks[1], &next_copy, sizeof(next_copy));
344  atomic_inc64(&copy_queue);
345  } else {
346  if (!copyFile(src, src_ident, dest, dest_data, pstats)) {
347  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to copy %s->%s : %d : %s",
348  entry, dest_data, errno, strerror(errno));
349  errno = 0;
350  result = false;
351  }
352  }
354  free(src_ident);
355  } else {
358  }
359 
360  // The target always exists (either previously existed or just created).
361  // This is not done in parallel like copyFile
362  if (result && dest->do_link(dest->context_, entry, dest_data)) {
363  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to link %s->%s : %d : %s", entry,
364  dest_data, errno, strerror(errno));
365  errno = 0;
366  result = false;
367  }
368  free(dest_data);
369  return result;
370 }
371 
372 bool handle_dir(struct fs_traversal *src,
373  struct cvmfs_attr *src_st,
374  struct fs_traversal *dest,
375  struct cvmfs_attr *dest_st,
376  const char *entry) {
377  src_st->st_mode |= S_IWUSR;
378  if (dest->do_mkdir(dest->context_, entry, src_st)) {
379  if (errno == EEXIST) {
380  errno = 0;
381  if (dest->set_meta(dest->context_, entry, src_st)) {
382  LogCvmfs(kLogCvmfs, kLogStderr, "Traversal failed to set_meta %s",
383  entry);
384  return false;
385  }
386  } else {
387  LogCvmfs(kLogCvmfs, kLogStderr, "Traversal failed to mkdir %s : %d : %s",
388  entry, errno, strerror(errno));
389  return false;
390  }
391  }
392  return true;
393 }
394 
395 void add_dir_for_sync(const char *dir, bool recursive) {
396  dirs_.push_back(new RecDir(dir, recursive));
397 }
398 
399 // Compares possibly null strings.
400 // If 1->handle destination
401 // If -1->handle source
402 // If 0->handle both
403 int robust_strcmp(const char *src, const char *dest) {
404  if (!src) {
405  return 1;
406  } else if (!dest) {
407  return -1;
408  } else {
409  return strcmp(src, dest);
410  }
411 }
412 
413 bool Sync(const char *dir,
414  struct fs_traversal *src,
415  struct fs_traversal *dest,
416  bool recursive,
417  perf::Statistics *pstats) {
418  bool result = true;
419  int cmp = 0;
420 
421  char **src_dir = NULL;
422  size_t src_len = 0;
423  ssize_t src_iter = -1;
424  char *src_entry = NULL;
425  struct cvmfs_attr *src_st = cvmfs_attr_init();
426  list_src_dir(src, dir, &src_dir, &src_len);
427 
428  char **dest_dir = NULL;
429  size_t dest_len = 0;
430  ssize_t dest_iter = -1;
431  char *dest_entry = NULL;
432  struct cvmfs_attr *dest_st = cvmfs_attr_init();
433  dest->list_dir(dest->context_, dir, &dest_dir, &dest_len);
434  qsort(dest_dir, dest_len, sizeof(char *), strcmp_list);
435 
436  // While either have defined members and there hasn't been a failure
437  while (result) {
438  if (cmp <= 0) {
439  getNext(src, dir, src_dir, &src_entry, &src_iter, &src_st, true, true,
440  pstats);
441  }
442  if (cmp >= 0) {
443  getNext(dest, dir, dest_dir, &dest_entry, &dest_iter, &dest_st, false,
444  false, pstats);
445  } else {
446  // A destination entry was added
448  }
449 
450  // All entries in both have been visited
451  if (!src_entry && !dest_entry)
452  break;
453 
454  cmp = robust_strcmp(src_entry, dest_entry);
455 
456  if (cmp <= 0) {
457  // Compares stats to see if they are equivalent
458  if (cmp == 0
459  && cvmfs_attr_cmp(src_st, dest_st, dest)
460  // Also check internal hardlink consistency in destination file system
461  // where applicable:
462  && (dest_st->cvm_checksum == NULL
463  || dest->is_hash_consistent(dest->context_, dest_st))) {
464  if (S_ISDIR(src_st->st_mode) && recursive) {
465  add_dir_for_sync(src_entry, recursive);
466  }
467  continue;
468  }
469  // If not equal, bring dest up-to-date
470  switch (src_st->st_mode & S_IFMT) {
471  case S_IFREG:
472  if (!handle_file(src, src_st, dest, dest_st, src_entry, pstats))
473  result = false;
474  break;
475  case S_IFDIR:
476  if (!handle_dir(src, src_st, dest, dest_st, src_entry))
477  result = false;
478  if (result && recursive)
479  add_dir_for_sync(src_entry, recursive);
480  break;
481  case S_IFLNK:
482  // Should likely copy the source of the symlink target
483  if (dest->do_symlink(dest->context_, src_entry, src_st->cvm_symlink,
484  src_st)
485  != 0) {
487  "Traversal failed to symlink %s->%s : %d : %s", src_entry,
488  src_st->cvm_symlink, errno, strerror(errno));
489  result = false;
490  }
491  break;
492  case S_IFIFO:
493  // Ignore pipe, but continue
494  LogCvmfs(kLogCvmfs, kLogStderr, "Skipping pipe: %s", src_entry);
495  break;
496  default:
498  "Encountered unknown file type '%d' for source file %s",
499  src_st->st_mode & S_IFMT, src_entry);
500  result = false;
501  }
502  /* Dest contains something missing from Src */
503  } else {
504  switch (dest_st->st_mode & S_IFMT) {
505  case S_IFREG:
506  case S_IFLNK:
507  if (dest->do_unlink(dest->context_, dest_entry) != 0) {
508  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to unlink file %s",
509  dest_entry);
510  result = false;
511  }
512  break;
513  case S_IFDIR:
514  // This is recursive so that the contents of the directory
515  // are removed before trying to remove the directory. If
516  // tail recursed, do_rmdir will fail as there are still
517  // contents.
518  if (!Sync(dest_entry, src, dest, true, pstats)) {
519  result = false;
520  break;
521  }
522  if (dest->do_rmdir(dest->context_, dest_entry) != 0) {
523  LogCvmfs(kLogCvmfs, kLogStderr, "Failed to remove directory %s",
524  dest_entry);
525  result = false;
526  }
527  break;
528  default:
530  "Encountered unknown file type '%d' for destination file %s",
531  dest_st->st_mode & S_IFMT, dest_entry);
532  result = false;
533  }
534  }
535  }
536 
537  cvmfs_list_free(src_dir);
538  free(src_entry);
539  if (src_st)
540  cvmfs_attr_free(src_st);
541 
542  cvmfs_list_free(dest_dir);
543  free(dest_entry);
544  if (dest_st)
545  cvmfs_attr_free(dest_st);
546 
547  return result;
548 }
549 
550 bool SyncFull(struct fs_traversal *src,
551  struct fs_traversal *dest,
552  perf::Statistics *pstats,
553  uint64_t last_print_time) {
554  if (dirs_.empty()) {
555  dirs_.push_back(new RecDir("", true));
556  }
557  while (!dirs_.empty()) {
558  RecDir *next_dir = dirs_.back();
559  dirs_.pop_back();
560 
561  if (!Sync(next_dir->dir, src, dest, next_dir->recursive, pstats)) {
562  LogCvmfs(kLogCvmfs, kLogStderr, "File %s failed to copy\n",
563  next_dir->dir);
564  return false;
565  }
566 
567  if (stat_update_period_ > 0
568  && platform_monotonic_time() - last_print_time > stat_update_period_) {
570  pstats->PrintList(perf::Statistics::kPrintSimple).c_str());
571  last_print_time = platform_monotonic_time();
572  }
573 
574  delete next_dir;
575  }
576  return true;
577 }
578 
583  int parallel;
584 };
585 
589 };
590 
591 static void *MainWorker(void *data) {
593  data);
594  MainWorkerContext *mwc = sc->mwc;
595  perf::Counter *files_transferred = mwc->pstats->Lookup(
597 
598  while (1) {
599  FileCopy next_copy;
600  {
602  ReadPipe(pipe_chunks[0], &next_copy, sizeof(next_copy));
603  }
604  if (next_copy.IsTerminateJob())
605  break;
606 
607  if (!next_copy.src || !next_copy.dest) {
608  continue;
609  }
610  if (!copyFile(mwc->src_fs, next_copy.src, mwc->dest_fs, next_copy.dest,
611  mwc->pstats)) {
612  LogCvmfs(kLogCvmfs, kLogStderr, "File %s failed to copy\n",
613  next_copy.src);
614  }
615  files_transferred->Inc();
616 
617  // Noted in FileCopy: This is freed here to prevent the strings from being
618  // double freed when a FileCopy goes out of scope here and at WritePipe
619  free(next_copy.src);
620  free(next_copy.dest);
621 
622  atomic_dec64(&copy_queue);
623  }
624  return NULL;
625 }
626 
628  perf::Statistics *result = new perf::Statistics();
630  "Number of files from source repository");
632  "Number of directories from source repository");
634  "Number of symlinks from source repository");
636  "Byte count of projected repository");
638  "Number of file system entries processed in the source");
639  result->Register(
641  "Number of file system entries processed in the destination");
642  result->Register(
644  "Number of data files transferred from source to destination");
646  "Bytes transferred from source to destination");
648  "Number of files not copied due to deduplication");
650  "Number of bytes not copied due to deduplication");
651  return result;
652 }
653 
654 int SyncInit(struct fs_traversal *src,
655  struct fs_traversal *dest,
656  const char *base,
657  const char *spec,
658  uint64_t parallel,
659  uint64_t stat_period) {
660  num_parallel_ = parallel;
661  stat_update_period_ = stat_period;
662 
664 
665  // Initialization
666  atomic_init64(&copy_queue);
667 
668  pthread_t *workers = NULL;
669 
670  struct MainWorkerSpecificContext *specificWorkerContexts = NULL;
671 
672  MainWorkerContext *mwc = NULL;
673 
674  if (num_parallel_ > 0) {
675  workers = reinterpret_cast<pthread_t *>(
676  smalloc(sizeof(pthread_t) * num_parallel_));
677 
678  specificWorkerContexts = reinterpret_cast<
679  struct MainWorkerSpecificContext *>(
680  smalloc(sizeof(struct MainWorkerSpecificContext) * num_parallel_));
681 
682  mwc = new struct MainWorkerContext;
683  // Start Workers
685  LogCvmfs(kLogCvmfs, kLogStdout, "Starting %u workers", num_parallel_);
686  mwc->src_fs = src;
687  mwc->dest_fs = dest;
688  mwc->pstats = pstats;
689  mwc->parallel = num_parallel_;
690 
691  for (unsigned i = 0; i < num_parallel_; ++i) {
692  specificWorkerContexts[i].mwc = mwc;
693  specificWorkerContexts[i].num_thread = i;
694  int retval = pthread_create(
695  &workers[i], NULL, MainWorker,
696  static_cast<void *>(&(specificWorkerContexts[i])));
697  assert(retval == 0);
698  }
699  }
700 
701  if (spec) {
702  delete spec_tree_;
704  }
705 
706  uint64_t last_print_time = 0;
708  int result = !SyncFull(src, dest, pstats, last_print_time);
709 
710  while (atomic_read64(&copy_queue) != 0) {
711  if (platform_monotonic_time() - last_print_time > stat_update_period_) {
713  pstats->PrintList(perf::Statistics::kPrintSimple).c_str());
714  last_print_time = platform_monotonic_time();
715  }
716 
717  SafeSleepMs(100);
718  }
719 
720 
721  if (num_parallel_ > 0) {
722  LogCvmfs(kLogCvmfs, kLogStdout, "Stopping %u workers", num_parallel_);
723  for (unsigned i = 0; i < num_parallel_; ++i) {
724  FileCopy terminate_workers;
725  WritePipe(pipe_chunks[1], &terminate_workers, sizeof(terminate_workers));
726  }
727  for (unsigned i = 0; i < num_parallel_; ++i) {
728  int retval = pthread_join(workers[i], NULL);
729  assert(retval == 0);
730  }
732  delete workers;
733  delete specificWorkerContexts;
734  delete mwc;
735  }
737  pstats->PrintList(perf::Statistics::kPrintHeader).c_str());
738  delete pstats;
739 
740  delete spec_tree_;
741 
742  return result;
743 }
744 
745 int GarbageCollect(struct fs_traversal *fs) {
746  LogCvmfs(kLogCvmfs, kLogStdout, "Performing garbage collection...");
747  uint64_t start_time = platform_monotonic_time();
748  int retval = fs->garbage_collector(fs->context_);
749  uint64_t end_time = platform_monotonic_time();
750  LogCvmfs(kLogCvmfs, kLogStdout, "Garbage collection took %lu seconds.",
751  (end_time - start_time));
752  return retval;
753 }
754 
755 } // namespace shrinkwrap
char * cvm_symlink
Definition: libcvmfs.h:172
Counter * Register(const std::string &name, const std::string &desc)
Definition: statistics.cc:163
int64_t atomic_int64
Definition: atomic.h:18
bool SyncFull(struct fs_traversal *src, struct fs_traversal *dest, perf::Statistics *pstats, uint64_t last_print_time)
struct fs_traversal * FindInterface(const char *type)
Definition: fs_traversal.cc:96
char * cvm_name
Definition: libcvmfs.h:174
int(* do_fopen)(void *file_ctx, fs_open_type op_mode)
#define SHRINKWRAP_STAT_DATA_BYTES_DEDUPED
Definition: fs_traversal.h:24
bool cvmfs_attr_cmp(struct cvmfs_attr *src, struct cvmfs_attr *dest, struct fs_traversal *dest_fs)
void list_src_dir(struct fs_traversal *src, const char *dir, char ***buf, size_t *len)
#define SHRINKWRAP_STAT_COUNT_FILE
Definition: fs_traversal.h:15
void cvmfs_attr_free(struct cvmfs_attr *attr)
Definition: libcvmfs.cc:52
bool getNext(struct fs_traversal *fs, const char *dir, char **dir_list, char **entry, ssize_t *iter, struct cvmfs_attr **st, bool get_hash, bool is_src, perf::Statistics *pstats)
#define SHRINKWRAP_STAT_DATA_FILES
Definition: fs_traversal.h:21
static void * MainWorker(void *data)
int(* set_meta)(struct fs_traversal_context *ctx, const char *path, const struct cvmfs_attr *stat)
bool(* is_hash_consistent)(struct fs_traversal_context *ctx, const struct cvmfs_attr *stat)
static SpecTree * Create(const std::string &path)
Definition: spec_tree.cc:21
#define SHRINKWRAP_STAT_COUNT_SYMLINK
Definition: fs_traversal.h:17
struct fs_traversal * posix_get_interface()
Definition: interface.cc:121
struct fs_traversal * libcvmfs_get_interface()
#define SHRINKWRAP_STAT_COUNT_DIR
Definition: fs_traversal.h:16
assert((mem||(size==0))&&"Out Of Memory")
int(* do_unlink)(struct fs_traversal_context *ctx, const char *path)
bool handle_file(struct fs_traversal *src, struct cvmfs_attr *src_st, struct fs_traversal *dest, struct cvmfs_attr *dest_st, const char *entry, perf::Statistics *pstats)
int(* garbage_collector)(struct fs_traversal_context *ctx)
int strcmp_list(const void *a, const void *b)
Definition: fs_traversal.cc:41
struct fs_traversal * dest_fs
int(* do_rmdir)(struct fs_traversal_context *ctx, const char *path)
void MakePipe(int pipe_fd[2])
Definition: posix.cc:487
void *(* get_handle)(struct fs_traversal_context *ctx, const char *identifier)
#define SHRINKWRAP_STAT_DATA_BYTES
Definition: fs_traversal.h:22
perf::Statistics * GetSyncStatTemplate()
char * cvm_checksum
Definition: libcvmfs.h:171
void(* do_ffree)(void *file_ctx)
int SyncInit(struct fs_traversal *src, struct fs_traversal *dest, const char *base, const char *spec, uint64_t parallel, uint64_t stat_period)
void cvmfs_list_free(char **buf)
Definition: libcvmfs.cc:438
Counter * Lookup(const std::string &name) const
Definition: statistics.cc:63
int(* touch)(struct fs_traversal_context *ctx, const struct cvmfs_attr *stat)
int(* do_symlink)(struct fs_traversal_context *ctx, const char *src, const char *dest, const struct cvmfs_attr *stat_info)
struct fs_traversal * src_fs
int(* do_link)(struct fs_traversal_context *ctx, const char *path, const char *identifier)
gid_t st_gid
Definition: libcvmfs.h:161
void(* list_dir)(struct fs_traversal_context *ctx, const char *dir, char ***buf, size_t *len)
#define SHRINKWRAP_STAT_ENTRIES_SRC
Definition: fs_traversal.h:19
off_t st_size
Definition: libcvmfs.h:163
#define COPY_BUFFER_SIZE
void Sync()
Definition: repository.cc:803
bool updateStat(struct fs_traversal *fs, const char *entry, struct cvmfs_attr **st, bool get_hash)
void add_dir_for_sync(const char *dir, bool recursive)
uint64_t platform_monotonic_time()
int robust_strcmp(const char *src, const char *dest)
void Inc()
Definition: statistics.h:30
int(* do_fclose)(void *file_ctx)
int(* get_stat)(struct fs_traversal_context *ctx, const char *path, struct cvmfs_attr *stat, bool get_hash)
int64_t Xadd(const int64_t delta)
Definition: statistics.h:34
#define SHRINKWRAP_STAT_ENTRIES_DEST
Definition: fs_traversal.h:20
int(* do_fwrite)(void *file_ctx, const char *buff, size_t len)
Definition: mutex.h:42
bool copyFile(struct fs_traversal *src_fs, const char *src_name, struct fs_traversal *dest_fs, const char *dest_name, perf::Statistics *pstats)
std::string PrintList(const PrintOptions print_options)
Definition: statistics.cc:80
void GarbageCollect()
unsigned version
Definition: libcvmfs.h:152
uint64_t size
Definition: libcvmfs.h:153
int(* do_fread)(void *file_ctx, char *buff, size_t len, size_t *read_len)
uid_t st_uid
Definition: libcvmfs.h:160
void SafeSleepMs(const unsigned ms)
Definition: posix.cc:2024
int(* do_mkdir)(struct fs_traversal_context *ctx, const char *path, const struct cvmfs_attr *stat)
bool IsMatching(std::string path)
Definition: spec_tree.cc:46
#define SHRINKWRAP_STAT_DATA_FILES_DEDUPED
Definition: fs_traversal.h:23
int ListDir(const char *dir, char ***buf, size_t *len)
Definition: spec_tree.cc:196
char *(* get_identifier)(struct fs_traversal_context *ctx, const struct cvmfs_attr *stat)
char * get_full_path(const char *dir, const char *entry)
struct MainWorkerContext * mwc
void WritePipe(int fd, const void *buf, size_t nbyte)
Definition: posix.cc:496
void ReadPipe(int fd, void *buf, size_t nbyte)
Definition: posix.cc:508
void ClosePipe(int pipe_fd[2])
Definition: posix.cc:559
struct cvmfs_attr * cvmfs_attr_init()
Definition: libcvmfs.cc:39
bool handle_dir(struct fs_traversal *src, struct cvmfs_attr *src_st, struct fs_traversal *dest, struct cvmfs_attr *dest_st, const char *entry)
#define SHRINKWRAP_STAT_COUNT_BYTE
Definition: fs_traversal.h:18
struct fs_traversal_context * context_
Errors
Definition: cvmfs_fsck.cc:36
mode_t st_mode
Definition: libcvmfs.h:158
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545