/* Copyright (C) Cronosys, LLC 2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This file contains code used by the --link-by-hash option. */ #include "rsync.h" #include "inums.h" extern int protocol_version; extern char *link_by_hash_dir; extern char sender_file_sum[MAX_DIGEST_LEN]; char link_by_hash_extra_sum[MAX_DIGEST_LEN]; /* Only used when md4 sums are in the transfer */ #ifdef HAVE_LINK /* This function is always called after a file is received, so the * sender_file_sum buffer has whatever the last checksum was for the * transferred file. */ void link_by_hash(const char *fname, const char *fnametmp, struct file_struct *file) { STRUCT_STAT st; char *hashname, *last_slash, *num_str; const char *hex; int num = 0; /* We don't bother to hard-link 0-length files. */ if (F_LENGTH(file) == 0) return; hex = sum_as_hex(5, protocol_version >= 30 ? sender_file_sum : link_by_hash_extra_sum, 0); if (asprintf(&hashname, "%s/%.3s/%.3s/%.3s/%s.%s.000000", link_by_hash_dir, hex, hex+3, hex+6, hex+9, big_num(F_LENGTH(file))) < 0) { out_of_memory("make_hash_name"); } last_slash = strrchr(hashname, '/'); num_str = strrchr(last_slash, '.') + 1; while (1) { if (num >= 999999) { /* Surely we'll never reach this... */ if (DEBUG_GTE(HASHLINK, 1)) rprintf(FINFO, "link-by-hash: giving up after \"%s\".\n", hashname); goto cleanup; } if (num > 0 && DEBUG_GTE(HASHLINK, 1)) rprintf(FINFO, "link-by-hash: max link count exceeded, starting new file \"%s\".\n", hashname); snprintf(num_str, 7, "%d", num++); if (do_stat(hashname, &st) < 0) break; if (do_link(hashname, fnametmp) < 0) { if (errno == EMLINK) continue; rsyserr(FERROR, errno, "link \"%s\" -> \"%s\"", hashname, full_fname(fname)); } else { if (DEBUG_GTE(HASHLINK, 2)) rprintf(FINFO, "link-by-hash (existing): \"%s\" -> %s\n", hashname, full_fname(fname)); robust_rename(fnametmp, fname, NULL, 0644); } goto cleanup; } if (DEBUG_GTE(HASHLINK, 2)) rprintf(FINFO, "link-by-hash (new): %s -> \"%s\"\n", full_fname(fname), hashname); if (do_link(fname, hashname) < 0 && (errno != ENOENT || make_path(hashname, MKP_DROP_NAME) < 0 || do_link(fname, hashname) < 0)) rsyserr(FERROR, errno, "link \"%s\" -> \"%s\"", full_fname(fname), hashname); cleanup: free(hashname); } #endif