Annotation of embedaddon/rsync/hlink.c, revision 1.1.1.4

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>