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

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

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