Annotation of embedaddon/rsync/hlink.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Routines to support hard-linking.
3: *
4: * Copyright (C) 1996 Andrew Tridgell
5: * Copyright (C) 1996 Paul Mackerras
6: * Copyright (C) 2002 Martin Pool <mbp@samba.org>
1.1.1.2 ! misho 7: * Copyright (C) 2004-2013 Wayne Davison
1.1 misho 8: *
9: * This program is free software; you can redistribute it and/or modify
10: * it under the terms of the GNU General Public License as published by
11: * the Free Software Foundation; either version 3 of the License, or
12: * (at your option) any later version.
13: *
14: * This program is distributed in the hope that it will be useful,
15: * but WITHOUT ANY WARRANTY; without even the implied warranty of
16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: * GNU General Public License for more details.
18: *
19: * You should have received a copy of the GNU General Public License along
20: * with this program; if not, visit the http://fsf.org website.
21: */
22:
23: #include "rsync.h"
1.1.1.2 ! misho 24: #include "inums.h"
! 25: #include "ifuncs.h"
1.1 misho 26:
27: extern int dry_run;
28: extern int list_only;
29: extern int am_sender;
30: extern int inc_recurse;
31: extern int do_xfers;
32: extern int link_dest;
33: extern int preserve_acls;
34: extern int preserve_xattrs;
35: extern int protocol_version;
36: extern int remove_source_files;
37: extern int stdout_format_has_i;
38: extern int maybe_ATTRS_REPORT;
39: extern int unsort_ndx;
40: extern char *basis_dir[MAX_BASIS_DIRS+1];
41: extern struct file_list *cur_flist;
42:
43: #ifdef SUPPORT_HARD_LINKS
44:
45: /* Starting with protocol 30, we use a simple hashtable on the sending side
46: * for hashing the st_dev and st_ino info. The receiving side gets told
47: * (via flags and a "group index") which items are hard-linked together, so
48: * we can avoid the pool of dev+inode data. For incremental recursion mode,
49: * the receiver will use a ndx hash to remember old pathnames. */
50:
51: static struct hashtable *dev_tbl;
52:
53: static struct hashtable *prior_hlinks;
54:
55: static struct file_list *hlink_flist;
56:
57: void init_hard_links(void)
58: {
59: if (am_sender || protocol_version < 30)
60: dev_tbl = hashtable_create(16, 1);
61: else if (inc_recurse)
62: prior_hlinks = hashtable_create(1024, 0);
63: }
64:
65: struct ht_int64_node *idev_find(int64 dev, int64 ino)
66: {
67: static struct ht_int64_node *dev_node = NULL;
68: struct hashtable *tbl;
69:
70: /* Note that some OSes have a dev == 0, so increment to avoid storing a 0. */
71: if (!dev_node || dev_node->key != dev+1) {
72: /* We keep a separate hash table of inodes for every device. */
73: dev_node = hashtable_find(dev_tbl, dev+1, 1);
1.1.1.2 ! misho 74: if (!(tbl = dev_node->data)) {
1.1 misho 75: tbl = dev_node->data = hashtable_create(512, 1);
1.1.1.2 ! misho 76: if (DEBUG_GTE(HLINK, 3)) {
! 77: rprintf(FINFO,
! 78: "[%s] created hashtable for dev %s\n",
! 79: who_am_i(), big_num(dev));
! 80: }
! 81: }
1.1 misho 82: } else
83: tbl = dev_node->data;
84:
85: return hashtable_find(tbl, ino, 1);
86: }
87:
88: void idev_destroy(void)
89: {
90: int i;
91:
92: for (i = 0; i < dev_tbl->size; i++) {
93: struct ht_int32_node *node = HT_NODE(dev_tbl, dev_tbl->nodes, i);
94: if (node->data)
95: hashtable_destroy(node->data);
96: }
97:
98: hashtable_destroy(dev_tbl);
99: }
100:
101: static int hlink_compare_gnum(int *int1, int *int2)
102: {
103: struct file_struct *f1 = hlink_flist->sorted[*int1];
104: struct file_struct *f2 = hlink_flist->sorted[*int2];
105: int32 gnum1 = F_HL_GNUM(f1);
106: int32 gnum2 = F_HL_GNUM(f2);
107:
108: if (gnum1 != gnum2)
109: return gnum1 > gnum2 ? 1 : -1;
110:
111: return *int1 > *int2 ? 1 : -1;
112: }
113:
114: static void match_gnums(int32 *ndx_list, int ndx_count)
115: {
116: int32 from, prev;
117: struct file_struct *file, *file_next;
118: struct ht_int32_node *node = NULL;
119: int32 gnum, gnum_next;
120:
121: qsort(ndx_list, ndx_count, sizeof ndx_list[0],
122: (int (*)()) hlink_compare_gnum);
123:
124: for (from = 0; from < ndx_count; from++) {
125: file = hlink_flist->sorted[ndx_list[from]];
126: gnum = F_HL_GNUM(file);
127: if (inc_recurse) {
128: node = hashtable_find(prior_hlinks, gnum, 1);
129: if (!node->data) {
130: if (!(node->data = new_array0(char, 5)))
131: out_of_memory("match_gnums");
132: assert(gnum >= hlink_flist->ndx_start);
133: file->flags |= FLAG_HLINK_FIRST;
134: prev = -1;
135: } else if (CVAL(node->data, 0) == 0) {
136: struct file_list *flist;
137: prev = IVAL(node->data, 1);
138: flist = flist_for_ndx(prev, NULL);
139: if (flist)
140: flist->files[prev - flist->ndx_start]->flags &= ~FLAG_HLINK_LAST;
141: else {
142: /* We skipped all prior files in this
143: * group, so mark this as a "first". */
144: file->flags |= FLAG_HLINK_FIRST;
145: prev = -1;
146: }
147: } else
148: prev = -1;
149: } else {
150: file->flags |= FLAG_HLINK_FIRST;
151: prev = -1;
152: }
153: for ( ; from < ndx_count-1; file = file_next, gnum = gnum_next, from++) { /*SHARED ITERATOR*/
154: file_next = hlink_flist->sorted[ndx_list[from+1]];
155: gnum_next = F_HL_GNUM(file_next);
156: if (gnum != gnum_next)
157: break;
158: F_HL_PREV(file) = prev;
159: /* The linked list uses over-the-wire ndx values. */
160: if (unsort_ndx)
161: prev = F_NDX(file);
162: else
163: prev = ndx_list[from] + hlink_flist->ndx_start;
164: }
165: if (prev < 0 && !inc_recurse) {
166: /* Disable hard-link bit and set DONE so that
167: * HLINK_BUMP()-dependent values are unaffected. */
168: file->flags &= ~(FLAG_HLINKED | FLAG_HLINK_FIRST);
169: file->flags |= FLAG_HLINK_DONE;
170: continue;
171: }
172:
173: file->flags |= FLAG_HLINK_LAST;
174: F_HL_PREV(file) = prev;
175: if (inc_recurse && CVAL(node->data, 0) == 0) {
176: if (unsort_ndx)
177: prev = F_NDX(file);
178: else
179: prev = ndx_list[from] + hlink_flist->ndx_start;
180: SIVAL(node->data, 1, prev);
181: }
182: }
183: }
184:
185: /* Analyze the hard-links in the file-list by creating a list of all the
186: * items that have hlink data, sorting them, and matching up identical
187: * values into clusters. These will be a single linked list from last
188: * to first when we're done. */
189: void match_hard_links(struct file_list *flist)
190: {
191: if (!list_only && flist->used) {
192: int i, ndx_count = 0;
193: int32 *ndx_list;
194:
195: if (!(ndx_list = new_array(int32, flist->used)))
196: out_of_memory("match_hard_links");
197:
198: for (i = 0; i < flist->used; i++) {
199: if (F_IS_HLINKED(flist->sorted[i]))
200: ndx_list[ndx_count++] = i;
201: }
202:
203: hlink_flist = flist;
204:
205: if (ndx_count)
206: match_gnums(ndx_list, ndx_count);
207:
208: free(ndx_list);
209: }
210: if (protocol_version < 30)
211: idev_destroy();
212: }
213:
214: static int maybe_hard_link(struct file_struct *file, int ndx,
1.1.1.2 ! misho 215: char *fname, int statret, stat_x *sxp,
1.1 misho 216: const char *oldname, STRUCT_STAT *old_stp,
217: const char *realname, int itemizing, enum logcode code)
218: {
219: if (statret == 0) {
220: if (sxp->st.st_dev == old_stp->st_dev
221: && sxp->st.st_ino == old_stp->st_ino) {
222: if (itemizing) {
223: itemize(fname, file, ndx, statret, sxp,
224: ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
225: 0, "");
226: }
1.1.1.2 ! misho 227: if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
1.1 misho 228: rprintf(FCLIENT, "%s is uptodate\n", fname);
229: file->flags |= FLAG_HLINK_DONE;
230: return 0;
231: }
232: }
233:
1.1.1.2 ! misho 234: if (atomic_create(file, fname, oldname, MAKEDEV(0, 0), sxp, statret == 0 ? DEL_FOR_FILE : 0)) {
1.1 misho 235: if (itemizing) {
236: itemize(fname, file, ndx, statret, sxp,
237: ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
238: realname);
239: }
1.1.1.2 ! misho 240: if (code != FNONE && INFO_GTE(NAME, 1))
1.1 misho 241: rprintf(code, "%s => %s\n", fname, realname);
242: return 0;
243: }
1.1.1.2 ! misho 244:
1.1 misho 245: return -1;
246: }
247:
248: /* Figure out if a prior entry is still there or if we just have a
249: * cached name for it. */
250: static char *check_prior(struct file_struct *file, int gnum,
251: int *prev_ndx_p, struct file_list **flist_p)
252: {
253: struct file_struct *fp;
254: struct ht_int32_node *node;
255: int prev_ndx = F_HL_PREV(file);
256:
257: while (1) {
258: struct file_list *flist;
259: if (prev_ndx < 0
260: || (flist = flist_for_ndx(prev_ndx, NULL)) == NULL)
261: break;
262: fp = flist->files[prev_ndx - flist->ndx_start];
263: if (!(fp->flags & FLAG_SKIP_HLINK)) {
264: *prev_ndx_p = prev_ndx;
265: *flist_p = flist;
266: return NULL;
267: }
268: F_HL_PREV(file) = prev_ndx = F_HL_PREV(fp);
269: }
270:
271: if (inc_recurse
272: && (node = hashtable_find(prior_hlinks, gnum, 0)) != NULL) {
273: assert(node->data != NULL);
274: if (CVAL(node->data, 0) != 0) {
275: *prev_ndx_p = -1;
276: *flist_p = NULL;
277: return node->data;
278: }
279: /* The prior file must have been skipped. */
280: F_HL_PREV(file) = -1;
281: }
282:
283: *prev_ndx_p = -1;
284: *flist_p = NULL;
285: return NULL;
286: }
287:
288: /* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not. Returns:
289: * 0 = process the file, 1 = skip the file, -1 = error occurred. */
1.1.1.2 ! misho 290: int hard_link_check(struct file_struct *file, int ndx, char *fname,
1.1 misho 291: int statret, stat_x *sxp, int itemizing,
292: enum logcode code)
293: {
294: STRUCT_STAT prev_st;
295: char namebuf[MAXPATHLEN], altbuf[MAXPATHLEN];
296: char *realname, *prev_name;
297: struct file_list *flist;
298: int gnum = inc_recurse ? F_HL_GNUM(file) : -1;
299: int prev_ndx;
300:
301: prev_name = realname = check_prior(file, gnum, &prev_ndx, &flist);
302:
303: if (!prev_name) {
304: struct file_struct *prev_file;
305:
306: if (!flist) {
307: /* The previous file was skipped, so this one is
308: * treated as if it were the first in its group. */
1.1.1.2 ! misho 309: if (DEBUG_GTE(HLINK, 2)) {
! 310: rprintf(FINFO, "hlink for %d (%s,%d): virtual first\n",
! 311: ndx, f_name(file, NULL), gnum);
! 312: }
1.1 misho 313: return 0;
314: }
315:
316: prev_file = flist->files[prev_ndx - flist->ndx_start];
317:
318: /* Is the previous link not complete yet? */
319: if (!(prev_file->flags & FLAG_HLINK_DONE)) {
320: /* Is the previous link being transferred? */
321: if (prev_file->flags & FLAG_FILE_SENT) {
322: /* Add ourselves to the list of files that will
323: * be updated when the transfer completes, and
324: * mark ourself as waiting for the transfer. */
325: F_HL_PREV(file) = F_HL_PREV(prev_file);
326: F_HL_PREV(prev_file) = ndx;
327: file->flags |= FLAG_FILE_SENT;
328: cur_flist->in_progress++;
1.1.1.2 ! misho 329: if (DEBUG_GTE(HLINK, 2)) {
! 330: rprintf(FINFO, "hlink for %d (%s,%d): waiting for %d\n",
! 331: ndx, f_name(file, NULL), gnum, F_HL_PREV(file));
! 332: }
1.1 misho 333: return 1;
334: }
1.1.1.2 ! misho 335: if (DEBUG_GTE(HLINK, 2)) {
! 336: rprintf(FINFO, "hlink for %d (%s,%d): looking for a leader\n",
! 337: ndx, f_name(file, NULL), gnum);
! 338: }
1.1 misho 339: return 0;
340: }
341:
342: /* There is a finished file to link with! */
343: if (!(prev_file->flags & FLAG_HLINK_FIRST)) {
344: /* The previous previous is FIRST when prev is not. */
345: prev_name = realname = check_prior(prev_file, gnum, &prev_ndx, &flist);
346: /* Update our previous pointer to point to the FIRST. */
347: F_HL_PREV(file) = prev_ndx;
348: }
349:
350: if (!prev_name) {
351: int alt_dest;
352:
1.1.1.2 ! misho 353: assert(flist != NULL);
1.1 misho 354: prev_file = flist->files[prev_ndx - flist->ndx_start];
355: /* F_HL_PREV() is alt_dest value when DONE && FIRST. */
356: alt_dest = F_HL_PREV(prev_file);
1.1.1.2 ! misho 357: if (DEBUG_GTE(HLINK, 2)) {
! 358: rprintf(FINFO, "hlink for %d (%s,%d): found flist match (alt %d)\n",
! 359: ndx, f_name(file, NULL), gnum, alt_dest);
! 360: }
1.1 misho 361:
362: if (alt_dest >= 0 && dry_run) {
363: pathjoin(namebuf, MAXPATHLEN, basis_dir[alt_dest],
364: f_name(prev_file, NULL));
365: prev_name = namebuf;
366: realname = f_name(prev_file, altbuf);
367: } else {
368: prev_name = f_name(prev_file, namebuf);
369: realname = prev_name;
370: }
371: }
372: }
373:
1.1.1.2 ! misho 374: if (DEBUG_GTE(HLINK, 2)) {
! 375: rprintf(FINFO, "hlink for %d (%s,%d): leader is %d (%s)\n",
! 376: ndx, f_name(file, NULL), gnum, prev_ndx, prev_name);
! 377: }
! 378:
1.1 misho 379: if (link_stat(prev_name, &prev_st, 0) < 0) {
380: if (!dry_run || errno != ENOENT) {
381: rsyserr(FERROR_XFER, errno, "stat %s failed", full_fname(prev_name));
382: return -1;
383: }
384: /* A new hard-link will get a new dev & inode, so approximate
385: * those values in dry-run mode by zeroing them. */
386: memset(&prev_st, 0, sizeof prev_st);
387: }
388:
389: if (statret < 0 && basis_dir[0] != NULL) {
390: /* If we match an alt-dest item, we don't output this as a change. */
391: char cmpbuf[MAXPATHLEN];
392: stat_x alt_sx;
393: int j = 0;
1.1.1.2 ! misho 394: init_stat_x(&alt_sx);
1.1 misho 395: do {
396: pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
397: if (link_stat(cmpbuf, &alt_sx.st, 0) < 0)
398: continue;
399: if (link_dest) {
400: if (prev_st.st_dev != alt_sx.st.st_dev
401: || prev_st.st_ino != alt_sx.st.st_ino)
402: continue;
403: statret = 1;
404: if (stdout_format_has_i == 0
1.1.1.2 ! misho 405: || (!INFO_GTE(NAME, 2) && stdout_format_has_i < 2)) {
1.1 misho 406: itemizing = 0;
407: code = FNONE;
1.1.1.2 ! misho 408: if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
1.1 misho 409: rprintf(FCLIENT, "%s is uptodate\n", fname);
410: }
411: break;
412: }
413: if (!unchanged_file(cmpbuf, file, &alt_sx.st))
414: continue;
415: statret = 1;
416: if (unchanged_attrs(cmpbuf, file, &alt_sx))
417: break;
418: } while (basis_dir[++j] != NULL);
419: if (statret == 1) {
420: sxp->st = alt_sx.st;
421: #ifdef SUPPORT_ACLS
422: if (preserve_acls && !S_ISLNK(file->mode)) {
423: free_acl(sxp);
424: if (!ACL_READY(alt_sx))
425: get_acl(cmpbuf, sxp);
426: else {
427: sxp->acc_acl = alt_sx.acc_acl;
428: sxp->def_acl = alt_sx.def_acl;
429: alt_sx.acc_acl = alt_sx.def_acl = NULL;
430: }
431: }
432: #endif
433: #ifdef SUPPORT_XATTRS
434: if (preserve_xattrs) {
435: free_xattr(sxp);
436: if (!XATTR_READY(alt_sx))
437: get_xattr(cmpbuf, sxp);
438: else {
439: sxp->xattr = alt_sx.xattr;
440: alt_sx.xattr = NULL;
441: }
442: }
443: #endif
1.1.1.2 ! misho 444: } else
! 445: free_stat_x(&alt_sx);
1.1 misho 446: }
447:
448: if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
449: realname, itemizing, code) < 0)
450: return -1;
451:
452: if (remove_source_files == 1 && do_xfers)
453: send_msg_int(MSG_SUCCESS, ndx);
454:
455: return 1;
456: }
457:
458: int hard_link_one(struct file_struct *file, const char *fname,
459: const char *oldname, int terse)
460: {
461: if (do_link(oldname, fname) < 0) {
462: enum logcode code;
463: if (terse) {
1.1.1.2 ! misho 464: if (!INFO_GTE(NAME, 1))
1.1 misho 465: return 0;
466: code = FINFO;
467: } else
468: code = FERROR_XFER;
469: rsyserr(code, errno, "link %s => %s failed",
470: full_fname(fname), oldname);
471: return 0;
472: }
473:
474: file->flags |= FLAG_HLINK_DONE;
475:
476: return 1;
477: }
478:
479: void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
480: STRUCT_STAT *stp, int itemizing, enum logcode code,
481: int alt_dest)
482: {
483: stat_x prev_sx;
484: STRUCT_STAT st;
485: char prev_name[MAXPATHLEN], alt_name[MAXPATHLEN];
486: const char *our_name;
487: struct file_list *flist;
488: int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
489:
490: if (stp == NULL && prev_ndx >= 0) {
1.1.1.2 ! misho 491: if (link_stat(fname, &st, 0) < 0 && !dry_run) {
1.1 misho 492: rsyserr(FERROR_XFER, errno, "stat %s failed",
493: full_fname(fname));
494: return;
495: }
496: stp = &st;
497: }
498:
499: /* FIRST combined with DONE means we were the first to get done. */
500: file->flags |= FLAG_HLINK_FIRST | FLAG_HLINK_DONE;
501: F_HL_PREV(file) = alt_dest;
502: if (alt_dest >= 0 && dry_run) {
503: pathjoin(alt_name, MAXPATHLEN, basis_dir[alt_dest],
504: f_name(file, NULL));
505: our_name = alt_name;
506: } else
507: our_name = fname;
508:
1.1.1.2 ! misho 509: init_stat_x(&prev_sx);
1.1 misho 510:
511: while ((ndx = prev_ndx) >= 0) {
512: int val;
513: flist = flist_for_ndx(ndx, "finish_hard_link");
514: file = flist->files[ndx - flist->ndx_start];
515: file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
516: prev_ndx = F_HL_PREV(file);
517: F_HL_PREV(file) = fin_ndx;
518: prev_statret = link_stat(f_name(file, prev_name), &prev_sx.st, 0);
519: val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
520: our_name, stp, fname, itemizing, code);
521: flist->in_progress--;
1.1.1.2 ! misho 522: free_stat_x(&prev_sx);
1.1 misho 523: if (val < 0)
524: continue;
525: if (remove_source_files == 1 && do_xfers)
526: send_msg_int(MSG_SUCCESS, ndx);
527: }
528:
529: if (inc_recurse) {
530: int gnum = F_HL_GNUM(file);
531: struct ht_int32_node *node = hashtable_find(prior_hlinks, gnum, 0);
532: if (node == NULL) {
533: rprintf(FERROR, "Unable to find a hlink node for %d (%s)\n", gnum, f_name(file, prev_name));
534: exit_cleanup(RERR_MESSAGEIO);
535: }
536: if (node->data == NULL) {
537: rprintf(FERROR, "Hlink node data for %d is NULL (%s)\n", gnum, f_name(file, prev_name));
538: exit_cleanup(RERR_MESSAGEIO);
539: }
540: if (CVAL(node->data, 0) != 0) {
541: rprintf(FERROR, "Hlink node data for %d already has path=%s (%s)\n",
542: gnum, (char*)node->data, f_name(file, prev_name));
543: exit_cleanup(RERR_MESSAGEIO);
544: }
545: free(node->data);
546: if (!(node->data = strdup(our_name)))
547: out_of_memory("finish_hard_link");
548: }
549: }
550:
551: int skip_hard_link(struct file_struct *file, struct file_list **flist_p)
552: {
553: struct file_list *flist;
554: int prev_ndx;
555:
556: file->flags |= FLAG_SKIP_HLINK;
557: if (!(file->flags & FLAG_HLINK_LAST))
558: return -1;
559:
560: check_prior(file, F_HL_GNUM(file), &prev_ndx, &flist);
561: if (prev_ndx >= 0) {
562: file = flist->files[prev_ndx - flist->ndx_start];
563: if (file->flags & (FLAG_HLINK_DONE|FLAG_FILE_SENT))
564: return -1;
565: file->flags |= FLAG_HLINK_LAST;
566: *flist_p = flist;
567: }
568:
569: return prev_ndx;
570: }
571: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>