Annotation of embedaddon/rsync/hlink.c, revision 1.1.1.3
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.3 ! misho 7: * Copyright (C) 2004-2015 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.3 ! misho 234: if (atomic_create(file, fname, NULL, 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;
1.1.1.3 ! misho 429: sxp->nfs4_acl = alt_sx.nfs4_acl;
1.1 misho 430: alt_sx.acc_acl = alt_sx.def_acl = NULL;
1.1.1.3 ! misho 431: alt_sx.nfs4_acl = NULL;
1.1 misho 432: }
433: }
434: #endif
435: #ifdef SUPPORT_XATTRS
436: if (preserve_xattrs) {
437: free_xattr(sxp);
438: if (!XATTR_READY(alt_sx))
439: get_xattr(cmpbuf, sxp);
440: else {
441: sxp->xattr = alt_sx.xattr;
442: alt_sx.xattr = NULL;
443: }
444: }
445: #endif
1.1.1.2 misho 446: } else
447: free_stat_x(&alt_sx);
1.1 misho 448: }
449:
450: if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
451: realname, itemizing, code) < 0)
452: return -1;
453:
454: if (remove_source_files == 1 && do_xfers)
455: send_msg_int(MSG_SUCCESS, ndx);
456:
457: return 1;
458: }
459:
460: int hard_link_one(struct file_struct *file, const char *fname,
461: const char *oldname, int terse)
462: {
463: if (do_link(oldname, fname) < 0) {
464: enum logcode code;
465: if (terse) {
1.1.1.2 misho 466: if (!INFO_GTE(NAME, 1))
1.1 misho 467: return 0;
468: code = FINFO;
469: } else
470: code = FERROR_XFER;
471: rsyserr(code, errno, "link %s => %s failed",
472: full_fname(fname), oldname);
473: return 0;
474: }
475:
476: file->flags |= FLAG_HLINK_DONE;
477:
478: return 1;
479: }
480:
481: void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
482: STRUCT_STAT *stp, int itemizing, enum logcode code,
483: int alt_dest)
484: {
485: stat_x prev_sx;
486: STRUCT_STAT st;
487: char prev_name[MAXPATHLEN], alt_name[MAXPATHLEN];
488: const char *our_name;
489: struct file_list *flist;
490: int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
491:
492: if (stp == NULL && prev_ndx >= 0) {
1.1.1.2 misho 493: if (link_stat(fname, &st, 0) < 0 && !dry_run) {
1.1 misho 494: rsyserr(FERROR_XFER, errno, "stat %s failed",
495: full_fname(fname));
496: return;
497: }
498: stp = &st;
499: }
500:
501: /* FIRST combined with DONE means we were the first to get done. */
502: file->flags |= FLAG_HLINK_FIRST | FLAG_HLINK_DONE;
503: F_HL_PREV(file) = alt_dest;
504: if (alt_dest >= 0 && dry_run) {
505: pathjoin(alt_name, MAXPATHLEN, basis_dir[alt_dest],
506: f_name(file, NULL));
507: our_name = alt_name;
508: } else
509: our_name = fname;
510:
1.1.1.2 misho 511: init_stat_x(&prev_sx);
1.1 misho 512:
513: while ((ndx = prev_ndx) >= 0) {
514: int val;
515: flist = flist_for_ndx(ndx, "finish_hard_link");
516: file = flist->files[ndx - flist->ndx_start];
517: file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
518: prev_ndx = F_HL_PREV(file);
519: F_HL_PREV(file) = fin_ndx;
520: prev_statret = link_stat(f_name(file, prev_name), &prev_sx.st, 0);
521: val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
522: our_name, stp, fname, itemizing, code);
523: flist->in_progress--;
1.1.1.2 misho 524: free_stat_x(&prev_sx);
1.1 misho 525: if (val < 0)
526: continue;
527: if (remove_source_files == 1 && do_xfers)
528: send_msg_int(MSG_SUCCESS, ndx);
529: }
530:
531: if (inc_recurse) {
532: int gnum = F_HL_GNUM(file);
533: struct ht_int32_node *node = hashtable_find(prior_hlinks, gnum, 0);
534: if (node == NULL) {
535: rprintf(FERROR, "Unable to find a hlink node for %d (%s)\n", gnum, f_name(file, prev_name));
536: exit_cleanup(RERR_MESSAGEIO);
537: }
538: if (node->data == NULL) {
539: rprintf(FERROR, "Hlink node data for %d is NULL (%s)\n", gnum, f_name(file, prev_name));
540: exit_cleanup(RERR_MESSAGEIO);
541: }
542: if (CVAL(node->data, 0) != 0) {
543: rprintf(FERROR, "Hlink node data for %d already has path=%s (%s)\n",
544: gnum, (char*)node->data, f_name(file, prev_name));
545: exit_cleanup(RERR_MESSAGEIO);
546: }
547: free(node->data);
548: if (!(node->data = strdup(our_name)))
549: out_of_memory("finish_hard_link");
550: }
551: }
552:
553: int skip_hard_link(struct file_struct *file, struct file_list **flist_p)
554: {
555: struct file_list *flist;
556: int prev_ndx;
557:
558: file->flags |= FLAG_SKIP_HLINK;
559: if (!(file->flags & FLAG_HLINK_LAST))
560: return -1;
561:
562: check_prior(file, F_HL_GNUM(file), &prev_ndx, &flist);
563: if (prev_ndx >= 0) {
564: file = flist->files[prev_ndx - flist->ndx_start];
565: if (file->flags & (FLAG_HLINK_DONE|FLAG_FILE_SENT))
566: return -1;
567: file->flags |= FLAG_HLINK_LAST;
568: *flist_p = flist;
569: }
570:
571: return prev_ndx;
572: }
573: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>