| Commit | Line | Data |
|---|---|---|
| 3a42736d MD |
1 | /*- |
| 2 | * CPDUP.C | |
| 3 | * | |
| 4 | * CPDUP <options> source destination | |
| 5 | * | |
| 6 | * (c) Copyright 1997-1999 by Matthew Dillon and Dima Ruban. Permission to | |
| 7 | * use and distribute based on the FreeBSD copyright. Supplied as-is, | |
| 8 | * USE WITH EXTREME CAUTION. | |
| 9 | * | |
| 10 | * This program attempts to duplicate the source onto the destination as | |
| 11 | * exactly as possible, retaining modify times, flags, perms, uid, and gid. | |
| 12 | * It can duplicate devices, files (including hardlinks), softlinks, | |
| 13 | * directories, and so forth. It is recursive by default! The duplication | |
| 14 | * is inclusive of removal of files/directories on the destination that do | |
| 15 | * not exist on the source. This program supports a per-directory exception | |
| 16 | * file called .cpignore, or a user-specified exception file. | |
| 17 | * | |
| 18 | * Safety features: | |
| 19 | * | |
| 20 | * - does not cross partition boundries on source | |
| 21 | * - asks for confirmation on deletions unless -i0 is specified | |
| 22 | * - refuses to replace a destination directory with a source file | |
| 23 | * unless -s0 is specified. | |
| 24 | * - terminates on error | |
| 25 | * | |
| 26 | * Copying features: | |
| 27 | * | |
| 28 | * - does not copy file if mtime, flags, perms, and size match unless | |
| 29 | * forced | |
| 30 | * | |
| 31 | * - copies to temporary and renames-over the original, allowing | |
| 32 | * you to update live systems | |
| 33 | * | |
| 34 | * - copies uid, gid, mtime, perms, flags, softlinks, devices, hardlinks, | |
| 35 | * and recurses through directories. | |
| 36 | * | |
| 37 | * - accesses a per-directory exclusion file, .cpignore, containing | |
| 38 | * standard wildcarded ( ? / * style, NOT regex) exclusions. | |
| 39 | * | |
| 40 | * - tries to play permissions and flags smart in regards to overwriting | |
| 41 | * schg files and doing related stuff. | |
| 42 | * | |
| 43 | * - Can do MD5 consistancy checks | |
| 44 | * | |
| 4e316ad5 MD |
45 | * - Is able to do incremental mirroring/backups via hardlinks from |
| 46 | * the 'previous' version (supplied with -H path). | |
| 47 | * | |
| 77133d96 | 48 | * $DragonFly: src/bin/cpdup/cpdup.c,v 1.32 2008/11/11 04:36:00 dillon Exp $ |
| 3a42736d MD |
49 | */ |
| 50 | ||
| 51 | /*- | |
| 52 | * Example: cc -O cpdup.c -o cpdup -lmd | |
| 53 | * | |
| 54 | * ".MD5.CHECKSUMS" contains md5 checksumms for the current directory. | |
| 55 | * This file is stored on the source. | |
| 56 | */ | |
| 57 | ||
| 58 | #include "cpdup.h" | |
| 4d858d58 MD |
59 | #include "hclink.h" |
| 60 | #include "hcproto.h" | |
| 3a42736d | 61 | |
| a2dc574c | 62 | #define HSIZE 8192 |
| 3a42736d | 63 | #define HMASK (HSIZE-1) |
| 4e316ad5 MD |
64 | #define HLSIZE 8192 |
| 65 | #define HLMASK (HLSIZE - 1) | |
| 3a42736d | 66 | |
| 975200d7 MD |
67 | #define MAXDEPTH 32 /* max copy depth for thread */ |
| 68 | #define GETBUFSIZE 8192 | |
| 69 | #define GETPATHSIZE 2048 | |
| 70 | #define GETLINKSIZE 1024 | |
| 71 | #define GETIOSIZE 65536 | |
| 72 | ||
| 4d858d58 MD |
73 | #ifndef _ST_FLAGS_PRESENT_ |
| 74 | #define st_flags st_mode | |
| 75 | #endif | |
| 76 | ||
| 3a42736d MD |
77 | typedef struct Node { |
| 78 | struct Node *no_Next; | |
| 79 | struct Node *no_HNext; | |
| 80 | int no_Value; | |
| 81 | char no_Name[4]; | |
| 82 | } Node; | |
| 83 | ||
| 84 | typedef struct List { | |
| 85 | Node li_Node; | |
| 86 | Node *li_Hash[HSIZE]; | |
| 87 | } List; | |
| 88 | ||
| 89 | struct hlink { | |
| 90 | ino_t ino; | |
| 91 | ino_t dino; | |
| 975200d7 | 92 | int refs; |
| 3a42736d MD |
93 | struct hlink *next; |
| 94 | struct hlink *prev; | |
| 02141299 | 95 | nlink_t nlinked; |
| 71de6efc | 96 | char name[0]; |
| 3a42736d MD |
97 | }; |
| 98 | ||
| a2dc574c MD |
99 | typedef struct copy_info { |
| 100 | char *spath; | |
| 101 | char *dpath; | |
| 102 | dev_t sdevNo; | |
| 103 | dev_t ddevNo; | |
| 104 | #ifdef USE_PTHREADS | |
| 105 | struct copy_info *parent; | |
| 106 | pthread_cond_t cond; | |
| 107 | int children; | |
| 108 | int r; | |
| 109 | #endif | |
| 110 | } *copy_info_t; | |
| 111 | ||
| 4e316ad5 | 112 | struct hlink *hltable[HLSIZE]; |
| 58860d7d | 113 | |
| 1c755102 | 114 | void RemoveRecur(const char *dpath, dev_t devNo); |
| 3a42736d MD |
115 | void InitList(List *list); |
| 116 | void ResetList(List *list); | |
| 117 | int AddList(List *list, const char *name, int n); | |
| fce2564b JR |
118 | static struct hlink *hltlookup(struct stat *); |
| 119 | static struct hlink *hltadd(struct stat *, const char *); | |
| 4e316ad5 | 120 | static char *checkHLPath(struct stat *st, const char *spath, const char *dpath); |
| d5fdcd00 | 121 | static int validate_check(const char *spath, const char *dpath); |
| fce2564b JR |
122 | static int shash(const char *s); |
| 123 | static void hltdelete(struct hlink *); | |
| 44dd1628 | 124 | static void hltsetdino(struct hlink *, ino_t); |
| 3a42736d | 125 | int YesNo(const char *path); |
| fce2564b JR |
126 | static int xrename(const char *src, const char *dst, u_long flags); |
| 127 | static int xlink(const char *src, const char *dst, u_long flags); | |
| 77133d96 | 128 | static int xremove(struct HostConf *host, const char *path); |
| 3a42736d | 129 | int WildCmp(const char *s1, const char *s2); |
| 975200d7 | 130 | static int DoCopy(copy_info_t info, int depth); |
| 3a42736d MD |
131 | |
| 132 | int AskConfirmation = 1; | |
| 133 | int SafetyOpt = 1; | |
| 577109ea | 134 | int ForceOpt; |
| ac482a7e | 135 | int DeviceOpt = 1; |
| 577109ea MD |
136 | int VerboseOpt; |
| 137 | int QuietOpt; | |
| 138 | int NoRemoveOpt; | |
| 139 | int UseMD5Opt; | |
| 140 | int UseFSMIDOpt; | |
| 141 | int SummaryOpt; | |
| 44dd1628 | 142 | int CompressOpt; |
| 4d858d58 | 143 | int SlaveOpt; |
| 577109ea | 144 | int EnableDirectoryRetries; |
| 4e316ad5 | 145 | int DstBaseLen; |
| d5fdcd00 | 146 | int ValidateOpt; |
| a2dc574c MD |
147 | int CurParallel; |
| 148 | int MaxParallel = -1; | |
| 975200d7 | 149 | int HardLinkCount; |
| 17e9c4cc MD |
150 | int RunningAsUser; |
| 151 | int RunningAsRoot; | |
| 1c755102 | 152 | const char *UseCpFile; |
| 4e316ad5 | 153 | const char *UseHLPath; |
| 577109ea MD |
154 | const char *MD5CacheFile; |
| 155 | const char *FSMIDCacheFile; | |
| 3a42736d | 156 | |
| 577109ea MD |
157 | int64_t CountSourceBytes; |
| 158 | int64_t CountSourceItems; | |
| 159 | int64_t CountCopiedItems; | |
| d5fdcd00 MD |
160 | int64_t CountSourceReadBytes; |
| 161 | int64_t CountTargetReadBytes; | |
| 577109ea MD |
162 | int64_t CountWriteBytes; |
| 163 | int64_t CountRemovedItems; | |
| d5fdcd00 | 164 | int64_t CountLinkedItems; |
| 1c755102 | 165 | |
| 4d858d58 MD |
166 | struct HostConf SrcHost; |
| 167 | struct HostConf DstHost; | |
| 168 | ||
| a2dc574c MD |
169 | #if USE_PTHREADS |
| 170 | pthread_mutex_t MasterMutex; | |
| 171 | #endif | |
| 172 | ||
| 3a42736d MD |
173 | int |
| 174 | main(int ac, char **av) | |
| 175 | { | |
| 176 | int i; | |
| 177 | char *src = NULL; | |
| 178 | char *dst = NULL; | |
| 4d858d58 | 179 | char *ptr; |
| 3a42736d | 180 | struct timeval start; |
| a2dc574c | 181 | struct copy_info info; |
| 3a42736d | 182 | |
| d8bce52d MD |
183 | signal(SIGPIPE, SIG_IGN); |
| 184 | ||
| 17e9c4cc MD |
185 | RunningAsUser = (geteuid() != 0); |
| 186 | RunningAsRoot = !RunningAsUser; | |
| 187 | ||
| a2dc574c | 188 | #if USE_PTHREADS |
| 713d03c0 MD |
189 | for (i = 0; i < HCTHASH_SIZE; ++i) { |
| 190 | pthread_mutex_init(&SrcHost.hct_mutex[i], NULL); | |
| 191 | pthread_mutex_init(&DstHost.hct_mutex[i], NULL); | |
| 192 | } | |
| a2dc574c MD |
193 | pthread_mutex_init(&MasterMutex, NULL); |
| 194 | pthread_mutex_lock(&MasterMutex); | |
| 195 | #endif | |
| 196 | ||
| 3a42736d MD |
197 | gettimeofday(&start, NULL); |
| 198 | for (i = 1; i < ac; ++i) { | |
| 3a42736d MD |
199 | int v = 1; |
| 200 | ||
| 4d858d58 | 201 | ptr = av[i]; |
| 3a42736d MD |
202 | if (*ptr != '-') { |
| 203 | if (src == NULL) { | |
| 204 | src = ptr; | |
| 205 | } else if (dst == NULL) { | |
| 206 | dst = ptr; | |
| 207 | } else { | |
| 208 | fatal("too many arguments"); | |
| 209 | /* not reached */ | |
| 210 | } | |
| 211 | continue; | |
| 212 | } | |
| 213 | ptr += 2; | |
| 214 | ||
| 215 | if (*ptr) | |
| 216 | v = strtol(ptr, NULL, 0); | |
| 217 | ||
| 218 | switch(ptr[-1]) { | |
| 44dd1628 MD |
219 | case 'C': |
| 220 | CompressOpt = 1; | |
| 221 | break; | |
| 3a42736d MD |
222 | case 'v': |
| 223 | VerboseOpt = 1; | |
| 224 | while (*ptr == 'v') { | |
| 225 | ++VerboseOpt; | |
| 226 | ++ptr; | |
| 227 | } | |
| 228 | if (*ptr >= '0' && *ptr <= '9') | |
| 229 | VerboseOpt = strtol(ptr, NULL, 0); | |
| 230 | break; | |
| a2dc574c MD |
231 | case 'l': |
| 232 | setlinebuf(stdout); | |
| 233 | setlinebuf(stderr); | |
| 234 | break; | |
| d5fdcd00 MD |
235 | case 'V': |
| 236 | ValidateOpt = v; | |
| 237 | break; | |
| 3a42736d MD |
238 | case 'I': |
| 239 | SummaryOpt = v; | |
| 240 | break; | |
| 241 | case 'o': | |
| 242 | NoRemoveOpt = v; | |
| 243 | break; | |
| 244 | case 'x': | |
| 245 | UseCpFile = ".cpignore"; | |
| 246 | break; | |
| 247 | case 'X': | |
| 248 | UseCpFile = (*ptr) ? ptr : av[++i]; | |
| 249 | break; | |
| 4e316ad5 MD |
250 | case 'H': |
| 251 | UseHLPath = (*ptr) ? ptr : av[++i]; | |
| 252 | break; | |
| 4d858d58 MD |
253 | case 'S': |
| 254 | SlaveOpt = v; | |
| 255 | break; | |
| 3a42736d MD |
256 | case 'f': |
| 257 | ForceOpt = v; | |
| 258 | break; | |
| 259 | case 'i': | |
| 260 | AskConfirmation = v; | |
| 261 | break; | |
| ac482a7e MD |
262 | case 'j': |
| 263 | DeviceOpt = v; | |
| 264 | break; | |
| a2dc574c MD |
265 | case 'p': |
| 266 | MaxParallel = v; | |
| 267 | break; | |
| 3a42736d MD |
268 | case 's': |
| 269 | SafetyOpt = v; | |
| 270 | break; | |
| 271 | case 'q': | |
| 272 | QuietOpt = v; | |
| 273 | break; | |
| 577109ea MD |
274 | case 'k': |
| 275 | UseFSMIDOpt = v; | |
| 276 | FSMIDCacheFile = ".FSMID.CHECK"; | |
| 277 | break; | |
| 278 | case 'K': | |
| 279 | UseFSMIDOpt = v; | |
| 280 | FSMIDCacheFile = av[++i]; | |
| 281 | break; | |
| 3a42736d MD |
282 | case 'M': |
| 283 | UseMD5Opt = v; | |
| 284 | MD5CacheFile = av[++i]; | |
| 285 | break; | |
| 286 | case 'm': | |
| 287 | UseMD5Opt = v; | |
| 288 | MD5CacheFile = ".MD5.CHECKSUMS"; | |
| 289 | break; | |
| 4d2076d3 CP |
290 | case 'u': |
| 291 | setvbuf(stdout, NULL, _IOLBF, 0); | |
| 292 | break; | |
| 3a42736d MD |
293 | default: |
| 294 | fatal("illegal option: %s\n", ptr - 2); | |
| 295 | /* not reached */ | |
| 296 | break; | |
| 297 | } | |
| 298 | } | |
| 299 | ||
| 300 | /* | |
| 4d858d58 MD |
301 | * If we are told to go into slave mode, run the HC protocol |
| 302 | */ | |
| 303 | if (SlaveOpt) { | |
| 304 | hc_slave(0, 1); | |
| 305 | exit(0); | |
| 306 | } | |
| 307 | ||
| 308 | /* | |
| 309 | * Extract the source and/or/neither target [user@]host and | |
| 310 | * make any required connections. | |
| 311 | */ | |
| 312 | if (src && (ptr = strchr(src, ':')) != NULL) { | |
| e823ea66 | 313 | asprintf(&SrcHost.host, "%*.*s", (int)(ptr - src), (int)(ptr - src), src); |
| 4d858d58 MD |
314 | src = ptr + 1; |
| 315 | if (UseCpFile) { | |
| 316 | fprintf(stderr, "The cpignore options are not currently supported for remote sources\n"); | |
| 317 | exit(1); | |
| 318 | } | |
| 319 | if (UseMD5Opt) { | |
| 320 | fprintf(stderr, "The MD5 options are not currently supported for remote sources\n"); | |
| 321 | exit(1); | |
| 322 | } | |
| e7624993 | 323 | if (hc_connect(&SrcHost) < 0) |
| 69301941 | 324 | exit(1); |
| 4d858d58 MD |
325 | } |
| 326 | if (dst && (ptr = strchr(dst, ':')) != NULL) { | |
| e823ea66 | 327 | asprintf(&DstHost.host, "%*.*s", (int)(ptr - dst), (int)(ptr - dst), dst); |
| 4d858d58 MD |
328 | dst = ptr + 1; |
| 329 | if (UseFSMIDOpt) { | |
| 330 | fprintf(stderr, "The FSMID options are not currently supported for remote targets\n"); | |
| 331 | exit(1); | |
| 332 | } | |
| 333 | if (hc_connect(&DstHost) < 0) | |
| e7624993 | 334 | exit(1); |
| 4d858d58 MD |
335 | } |
| 336 | ||
| 337 | /* | |
| 3a42736d MD |
338 | * dst may be NULL only if -m option is specified, |
| 339 | * which forces an update of the MD5 checksums | |
| 340 | */ | |
| 3a42736d MD |
341 | if (dst == NULL && UseMD5Opt == 0) { |
| 342 | fatal(NULL); | |
| 343 | /* not reached */ | |
| 344 | } | |
| d8fd8bea | 345 | bzero(&info, sizeof(info)); |
| 07424787 | 346 | #if USE_PTHREADS |
| a2dc574c MD |
347 | info.r = 0; |
| 348 | info.children = 0; | |
| 349 | pthread_cond_init(&info.cond, NULL); | |
| 350 | #endif | |
| 3a42736d | 351 | if (dst) { |
| 4e316ad5 | 352 | DstBaseLen = strlen(dst); |
| a2dc574c MD |
353 | info.spath = src; |
| 354 | info.dpath = dst; | |
| 355 | info.sdevNo = (dev_t)-1; | |
| 356 | info.ddevNo = (dev_t)-1; | |
| 975200d7 | 357 | i = DoCopy(&info, -1); |
| 3a42736d | 358 | } else { |
| a2dc574c MD |
359 | info.spath = src; |
| 360 | info.dpath = NULL; | |
| 361 | info.sdevNo = (dev_t)-1; | |
| 362 | info.ddevNo = (dev_t)-1; | |
| 975200d7 | 363 | i = DoCopy(&info, -1); |
| 3a42736d | 364 | } |
| a2dc574c MD |
365 | #if USE_PTHREADS |
| 366 | pthread_cond_destroy(&info.cond); | |
| 367 | #endif | |
| 4d858d58 | 368 | #ifndef NOMD5 |
| 3a42736d | 369 | md5_flush(); |
| 4d858d58 | 370 | #endif |
| 577109ea | 371 | fsmid_flush(); |
| 3a42736d MD |
372 | |
| 373 | if (SummaryOpt && i == 0) { | |
| 4a159c4c | 374 | double duration; |
| 3a42736d MD |
375 | struct timeval end; |
| 376 | ||
| 377 | gettimeofday(&end, NULL); | |
| d5fdcd00 MD |
378 | #if 0 |
| 379 | /* don't count stat's in our byte statistics */ | |
| 3a42736d | 380 | CountSourceBytes += sizeof(struct stat) * CountSourceItems; |
| d5fdcd00 | 381 | CountSourceReadBytes += sizeof(struct stat) * CountSourceItems; |
| 3a42736d MD |
382 | CountWriteBytes += sizeof(struct stat) * CountCopiedItems; |
| 383 | CountWriteBytes += sizeof(struct stat) * CountRemovedItems; | |
| d5fdcd00 | 384 | #endif |
| 3a42736d | 385 | |
| 4a159c4c MD |
386 | duration = (end.tv_sec - start.tv_sec); |
| 387 | duration += (double)(end.tv_usec - start.tv_usec) / 1000000.0; | |
| 388 | if (duration == 0.0) | |
| 389 | duration = 1.0; | |
| d0d91865 | 390 | logstd("cpdup completed successfully\n"); |
| d5fdcd00 MD |
391 | logstd("%lld bytes source, %lld src bytes read, %lld tgt bytes read\n" |
| 392 | "%lld bytes written (%.1fX speedup)\n", | |
| 3a42736d | 393 | (long long)CountSourceBytes, |
| d5fdcd00 MD |
394 | (long long)CountSourceReadBytes, |
| 395 | (long long)CountTargetReadBytes, | |
| 3a42736d | 396 | (long long)CountWriteBytes, |
| d5fdcd00 MD |
397 | ((double)CountSourceBytes * 2.0) / ((double)(CountSourceReadBytes + CountTargetReadBytes + CountWriteBytes))); |
| 398 | logstd("%lld source items, %lld items copied, %lld items linked, " | |
| 399 | "%lld things deleted\n", | |
| 3a42736d MD |
400 | (long long)CountSourceItems, |
| 401 | (long long)CountCopiedItems, | |
| d5fdcd00 | 402 | (long long)CountLinkedItems, |
| 3a42736d MD |
403 | (long long)CountRemovedItems); |
| 404 | logstd("%.1f seconds %5d Kbytes/sec synced %5d Kbytes/sec scanned\n", | |
| 4a159c4c MD |
405 | duration, |
| 406 | (int)((CountSourceReadBytes + CountTargetReadBytes + CountWriteBytes) / duration / 1024.0), | |
| 407 | (int)(CountSourceBytes / duration / 1024.0)); | |
| 3a42736d MD |
408 | } |
| 409 | exit((i == 0) ? 0 : 1); | |
| 410 | } | |
| 411 | ||
| fce2564b | 412 | static struct hlink * |
| 3a42736d MD |
413 | hltlookup(struct stat *stp) |
| 414 | { | |
| 07424787 | 415 | #if USE_PTHREADS |
| 44dd1628 | 416 | struct timespec ts = { 0, 100000 }; |
| 07424787 | 417 | #endif |
| 3a42736d MD |
418 | struct hlink *hl; |
| 419 | int n; | |
| 420 | ||
| 4e316ad5 | 421 | n = stp->st_ino & HLMASK; |
| 3a42736d | 422 | |
| 44dd1628 MD |
423 | #if USE_PTHREADS |
| 424 | again: | |
| 425 | #endif | |
| 426 | for (hl = hltable[n]; hl; hl = hl->next) { | |
| 427 | if (hl->ino == stp->st_ino) { | |
| 428 | #if USE_PTHREADS | |
| 429 | /* | |
| 430 | * If the hl entry is still in the process of being created | |
| 431 | * by another thread we have to wait until it has either been | |
| 432 | * deleted or completed. | |
| 433 | */ | |
| 975200d7 | 434 | if (hl->refs) { |
| 44dd1628 MD |
435 | pthread_mutex_unlock(&MasterMutex); |
| 436 | nanosleep(&ts, NULL); | |
| 437 | pthread_mutex_lock(&MasterMutex); | |
| 438 | goto again; | |
| 439 | } | |
| 440 | #endif | |
| 975200d7 | 441 | ++hl->refs; |
| 44dd1628 MD |
442 | return hl; |
| 443 | } | |
| 444 | } | |
| 3a42736d MD |
445 | |
| 446 | return NULL; | |
| 447 | } | |
| 448 | ||
| fce2564b | 449 | static struct hlink * |
| 3a42736d MD |
450 | hltadd(struct stat *stp, const char *path) |
| 451 | { | |
| 452 | struct hlink *new; | |
| 71de6efc | 453 | int plen = strlen(path); |
| 3a42736d MD |
454 | int n; |
| 455 | ||
| 71de6efc MD |
456 | new = malloc(offsetof(struct hlink, name[plen + 1])); |
| 457 | if (new == NULL) { | |
| 3a42736d | 458 | fprintf(stderr, "out of memory\n"); |
| 58860d7d | 459 | exit(EXIT_FAILURE); |
| 3a42736d | 460 | } |
| 975200d7 | 461 | ++HardLinkCount; |
| 3a42736d MD |
462 | |
| 463 | /* initialize and link the new element into the table */ | |
| 464 | new->ino = stp->st_ino; | |
| 44dd1628 | 465 | new->dino = (ino_t)-1; |
| 975200d7 | 466 | new->refs = 1; |
| 71de6efc | 467 | bcopy(path, new->name, plen + 1); |
| 3a42736d MD |
468 | new->nlinked = 1; |
| 469 | new->prev = NULL; | |
| 4e316ad5 | 470 | n = stp->st_ino & HLMASK; |
| 3a42736d MD |
471 | new->next = hltable[n]; |
| 472 | if (hltable[n]) | |
| 473 | hltable[n]->prev = new; | |
| 474 | hltable[n] = new; | |
| 475 | ||
| 476 | return new; | |
| 477 | } | |
| 478 | ||
| fce2564b | 479 | static void |
| 44dd1628 MD |
480 | hltsetdino(struct hlink *hl, ino_t inum) |
| 481 | { | |
| 482 | hl->dino = inum; | |
| 483 | } | |
| 484 | ||
| 485 | static void | |
| 3a42736d MD |
486 | hltdelete(struct hlink *hl) |
| 487 | { | |
| 975200d7 MD |
488 | assert(hl->refs == 1); |
| 489 | --hl->refs; | |
| 3a42736d MD |
490 | if (hl->prev) { |
| 491 | if (hl->next) | |
| 492 | hl->next->prev = hl->prev; | |
| 493 | hl->prev->next = hl->next; | |
| 494 | } else { | |
| 495 | if (hl->next) | |
| 496 | hl->next->prev = NULL; | |
| 497 | ||
| 4e316ad5 | 498 | hltable[hl->ino & HLMASK] = hl->next; |
| 3a42736d | 499 | } |
| 975200d7 | 500 | --HardLinkCount; |
| 3a42736d MD |
501 | free(hl); |
| 502 | } | |
| 503 | ||
| 975200d7 MD |
504 | static void |
| 505 | hltrels(struct hlink *hl) | |
| 506 | { | |
| 507 | assert(hl->refs == 1); | |
| 508 | --hl->refs; | |
| 509 | } | |
| 510 | ||
| 4e316ad5 MD |
511 | /* |
| 512 | * If UseHLPath is defined check to see if the file in question is | |
| 513 | * the same as the source file, and if it is return a pointer to the | |
| 514 | * -H path based file for hardlinking. Else return NULL. | |
| 515 | */ | |
| 516 | static char * | |
| 517 | checkHLPath(struct stat *st1, const char *spath, const char *dpath) | |
| 518 | { | |
| 519 | struct stat sthl; | |
| 520 | char *hpath; | |
| d5fdcd00 | 521 | int error; |
| 4e316ad5 MD |
522 | |
| 523 | asprintf(&hpath, "%s%s", UseHLPath, dpath + DstBaseLen); | |
| 524 | ||
| 525 | /* | |
| 526 | * stat info matches ? | |
| 527 | */ | |
| 4d858d58 | 528 | if (hc_stat(&DstHost, hpath, &sthl) < 0 || |
| 4e316ad5 | 529 | st1->st_size != sthl.st_size || |
| 17e9c4cc MD |
530 | st1->st_mtime != sthl.st_mtime || |
| 531 | (RunningAsRoot && (st1->st_uid != sthl.st_uid || | |
| 532 | st1->st_gid != sthl.st_gid)) | |
| 4e316ad5 MD |
533 | ) { |
| 534 | free(hpath); | |
| 535 | return(NULL); | |
| 536 | } | |
| 537 | ||
| 538 | /* | |
| d5fdcd00 | 539 | * If ForceOpt or ValidateOpt is set we have to compare the files |
| 4e316ad5 | 540 | */ |
| d5fdcd00 MD |
541 | if (ForceOpt || ValidateOpt) { |
| 542 | error = validate_check(spath, hpath); | |
| 543 | if (error) { | |
| 4e316ad5 MD |
544 | free(hpath); |
| 545 | hpath = NULL; | |
| 546 | } | |
| 547 | } | |
| 548 | return(hpath); | |
| 549 | } | |
| 550 | ||
| d5fdcd00 MD |
551 | /* |
| 552 | * Return 0 if the contents of the file <spath> matches the contents of | |
| 553 | * the file <dpath>. | |
| 554 | */ | |
| 555 | static int | |
| 556 | validate_check(const char *spath, const char *dpath) | |
| 557 | { | |
| 558 | int error; | |
| 559 | int fd1; | |
| 560 | int fd2; | |
| 561 | ||
| 562 | fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0); | |
| 563 | fd2 = hc_open(&DstHost, dpath, O_RDONLY, 0); | |
| 564 | error = -1; | |
| 565 | ||
| 566 | if (fd1 >= 0 && fd2 >= 0) { | |
| 567 | int n; | |
| 568 | int x; | |
| 975200d7 MD |
569 | char *iobuf1 = malloc(GETIOSIZE); |
| 570 | char *iobuf2 = malloc(GETIOSIZE); | |
| d5fdcd00 | 571 | |
| 975200d7 | 572 | while ((n = hc_read(&SrcHost, fd1, iobuf1, GETIOSIZE)) > 0) { |
| d5fdcd00 | 573 | CountSourceReadBytes += n; |
| 975200d7 | 574 | x = hc_read(&DstHost, fd2, iobuf2, GETIOSIZE); |
| d5fdcd00 MD |
575 | if (x > 0) |
| 576 | CountTargetReadBytes += x; | |
| 577 | if (x != n) | |
| 578 | break; | |
| 975200d7 | 579 | if (bcmp(iobuf1, iobuf2, n) != 0) |
| d5fdcd00 MD |
580 | break; |
| 581 | } | |
| 975200d7 MD |
582 | free(iobuf1); |
| 583 | free(iobuf2); | |
| d5fdcd00 MD |
584 | if (n == 0) |
| 585 | error = 0; | |
| 586 | } | |
| 587 | if (fd1 >= 0) | |
| 588 | hc_close(&SrcHost, fd1); | |
| 589 | if (fd2 >= 0) | |
| 590 | hc_close(&DstHost, fd2); | |
| 591 | return (error); | |
| 592 | } | |
| a2dc574c MD |
593 | #if USE_PTHREADS |
| 594 | ||
| 595 | static void * | |
| 596 | DoCopyThread(void *arg) | |
| 597 | { | |
| 598 | copy_info_t cinfo = arg; | |
| 599 | char *spath = cinfo->spath; | |
| 600 | char *dpath = cinfo->dpath; | |
| 0509810d | 601 | int r; |
| a2dc574c | 602 | |
| 0509810d MD |
603 | r = pthread_detach(pthread_self()); |
| 604 | assert(r == 0); | |
| a2dc574c MD |
605 | pthread_cond_init(&cinfo->cond, NULL); |
| 606 | pthread_mutex_lock(&MasterMutex); | |
| 975200d7 | 607 | cinfo->r += DoCopy(cinfo, 0); |
| a2dc574c MD |
608 | /* cinfo arguments invalid on return */ |
| 609 | --cinfo->parent->children; | |
| 610 | --CurParallel; | |
| 611 | pthread_cond_signal(&cinfo->parent->cond); | |
| a2dc574c MD |
612 | free(spath); |
| 613 | if (dpath) | |
| 614 | free(dpath); | |
| 615 | pthread_cond_destroy(&cinfo->cond); | |
| 616 | free(cinfo); | |
| 975200d7 MD |
617 | hcc_free_trans(&SrcHost); |
| 618 | hcc_free_trans(&DstHost); | |
| d8fd8bea | 619 | pthread_mutex_unlock(&MasterMutex); |
| a2dc574c MD |
620 | return(NULL); |
| 621 | } | |
| 622 | ||
| 623 | #endif | |
| d5fdcd00 | 624 | |
| 3a42736d | 625 | int |
| 975200d7 | 626 | DoCopy(copy_info_t info, int depth) |
| 3a42736d | 627 | { |
| a2dc574c MD |
628 | const char *spath = info->spath; |
| 629 | const char *dpath = info->dpath; | |
| 630 | dev_t sdevNo = info->sdevNo; | |
| 631 | dev_t ddevNo = info->ddevNo; | |
| 3a42736d MD |
632 | struct stat st1; |
| 633 | struct stat st2; | |
| 17e9c4cc | 634 | unsigned long st2_flags; |
| 577109ea | 635 | int r, mres, fres, st2Valid; |
| 58860d7d | 636 | struct hlink *hln; |
| a2dc574c | 637 | List *list = malloc(sizeof(List)); |
| 58860d7d | 638 | u_int64_t size; |
| 3a42736d | 639 | |
| a2dc574c | 640 | InitList(list); |
| 577109ea | 641 | r = mres = fres = st2Valid = 0; |
| 17e9c4cc | 642 | st2_flags = 0; |
| 58860d7d MD |
643 | size = 0; |
| 644 | hln = NULL; | |
| 645 | ||
| a2dc574c MD |
646 | if (hc_lstat(&SrcHost, spath, &st1) != 0) { |
| 647 | r = 0; | |
| 648 | goto done; | |
| 649 | } | |
| 3a42736d MD |
650 | st2.st_mode = 0; /* in case lstat fails */ |
| 651 | st2.st_flags = 0; /* in case lstat fails */ | |
| 17e9c4cc | 652 | if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0) { |
| 3a42736d | 653 | st2Valid = 1; |
| 17e9c4cc MD |
654 | #ifdef _ST_FLAGS_PRESENT_ |
| 655 | st2_flags = st2.st_flags; | |
| 656 | #endif | |
| 657 | } | |
| 3a42736d MD |
658 | |
| 659 | if (S_ISREG(st1.st_mode)) { | |
| d5fdcd00 | 660 | size = st1.st_size; |
| 3a42736d MD |
661 | } |
| 662 | ||
| 663 | /* | |
| 664 | * Handle hardlinks | |
| 665 | */ | |
| 666 | ||
| 667 | if (S_ISREG(st1.st_mode) && st1.st_nlink > 1 && dpath) { | |
| 668 | if ((hln = hltlookup(&st1)) != NULL) { | |
| 669 | hln->nlinked++; | |
| 670 | ||
| 671 | if (st2Valid) { | |
| 672 | if (st2.st_ino == hln->dino) { | |
| 673 | /* | |
| 674 | * hard link is already correct, nothing to do | |
| 675 | */ | |
| 676 | if (VerboseOpt >= 3) | |
| 677 | logstd("%-32s nochange\n", (dpath) ? dpath : spath); | |
| 975200d7 | 678 | if (hln->nlinked == st1.st_nlink) { |
| 3a42736d | 679 | hltdelete(hln); |
| 975200d7 MD |
680 | hln = NULL; |
| 681 | } | |
| 3a42736d | 682 | CountSourceItems++; |
| a2dc574c MD |
683 | r = 0; |
| 684 | goto done; | |
| 3a42736d MD |
685 | } else { |
| 686 | /* | |
| 687 | * hard link is not correct, attempt to unlink it | |
| 688 | */ | |
| 77133d96 | 689 | if (xremove(&DstHost, dpath) < 0) { |
| 3a42736d MD |
690 | logerr("%-32s hardlink: unable to unlink: %s\n", |
| 691 | ((dpath) ? dpath : spath), strerror(errno)); | |
| 692 | hltdelete(hln); | |
| 975200d7 MD |
693 | hln = NULL; |
| 694 | ++r; | |
| 695 | goto done; | |
| 3a42736d MD |
696 | } |
| 697 | } | |
| 698 | } | |
| 699 | ||
| 700 | if (xlink(hln->name, dpath, st1.st_flags) < 0) { | |
| 9b2d9484 | 701 | int tryrelink = (errno == EMLINK); |
| 3a42736d MD |
702 | logerr("%-32s hardlink: unable to link to %s: %s\n", |
| 703 | (dpath ? dpath : spath), hln->name, strerror(errno) | |
| 704 | ); | |
| 705 | hltdelete(hln); | |
| 706 | hln = NULL; | |
| 9b2d9484 MD |
707 | if (tryrelink) { |
| 708 | logerr("%-20s hardlink: will attempt to copy normally\n"); | |
| 709 | goto relink; | |
| 710 | } | |
| 3a42736d MD |
711 | ++r; |
| 712 | } else { | |
| 713 | if (hln->nlinked == st1.st_nlink) { | |
| 714 | hltdelete(hln); | |
| 715 | hln = NULL; | |
| 716 | } | |
| 717 | if (r == 0) { | |
| 718 | if (VerboseOpt) { | |
| 719 | logstd("%-32s hardlink: %s\n", | |
| 720 | (dpath ? dpath : spath), | |
| 721 | (st2Valid ? "relinked" : "linked") | |
| 722 | ); | |
| 723 | } | |
| 724 | CountSourceItems++; | |
| 725 | CountCopiedItems++; | |
| a2dc574c MD |
726 | r = 0; |
| 727 | goto done; | |
| 3a42736d MD |
728 | } |
| 729 | } | |
| 730 | } else { | |
| 731 | /* | |
| 732 | * first instance of hardlink must be copied normally | |
| 733 | */ | |
| 9b2d9484 | 734 | relink: |
| 3a42736d MD |
735 | hln = hltadd(&st1, dpath); |
| 736 | } | |
| 737 | } | |
| 738 | ||
| 739 | /* | |
| 740 | * Do we need to copy the file/dir/link/whatever? Early termination | |
| 577109ea MD |
741 | * if we do not. Always redo links. Directories are always traversed |
| 742 | * except when the FSMID options are used. | |
| 3a42736d MD |
743 | * |
| 744 | * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good. | |
| 745 | */ | |
| 746 | ||
| 747 | if ( | |
| 4d858d58 MD |
748 | st2Valid |
| 749 | && st1.st_mode == st2.st_mode | |
| 750 | #ifdef _ST_FLAGS_PRESENT_ | |
| 17e9c4cc | 751 | && (RunningAsUser || st1.st_flags == st2.st_flags) |
| 4d858d58 | 752 | #endif |
| 3a42736d MD |
753 | ) { |
| 754 | if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) { | |
| 577109ea MD |
755 | /* |
| 756 | * If FSMID tracking is turned on we can avoid recursing through | |
| 757 | * an entire directory subtree if the FSMID matches. | |
| 758 | */ | |
| 759 | #ifdef _ST_FSMID_PRESENT_ | |
| 760 | if (ForceOpt == 0 && | |
| 761 | (UseFSMIDOpt && (fres = fsmid_check(st1.st_fsmid, dpath)) == 0) | |
| 762 | ) { | |
| 763 | if (VerboseOpt >= 3) { | |
| 764 | if (UseFSMIDOpt) | |
| 765 | logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath)); | |
| 766 | else | |
| 767 | logstd("%-32s nochange\n", (dpath ? dpath : spath)); | |
| 768 | } | |
| a2dc574c MD |
769 | r = 0; |
| 770 | goto done; | |
| 577109ea MD |
771 | } |
| 772 | #endif | |
| 3a42736d MD |
773 | } else { |
| 774 | if (ForceOpt == 0 && | |
| 775 | st1.st_size == st2.st_size && | |
| 17e9c4cc MD |
776 | st1.st_mtime == st2.st_mtime && |
| 777 | (RunningAsUser || (st1.st_uid == st2.st_uid && | |
| 778 | st1.st_gid == st2.st_gid)) | |
| 4d858d58 | 779 | #ifndef NOMD5 |
| d5fdcd00 MD |
780 | && (UseMD5Opt == 0 || !S_ISREG(st1.st_mode) || |
| 781 | (mres = md5_check(spath, dpath)) == 0) | |
| 4d858d58 | 782 | #endif |
| 577109ea | 783 | #ifdef _ST_FSMID_PRESENT_ |
| d5fdcd00 MD |
784 | && (UseFSMIDOpt == 0 || |
| 785 | (fres = fsmid_check(st1.st_fsmid, dpath)) == 0) | |
| 577109ea | 786 | #endif |
| d5fdcd00 MD |
787 | && (ValidateOpt == 0 || !S_ISREG(st1.st_mode) || |
| 788 | validate_check(spath, dpath) == 0) | |
| 3a42736d | 789 | ) { |
| 17e9c4cc MD |
790 | /* |
| 791 | * The files are identical, but if we are not running as | |
| 792 | * root we might need to adjust ownership/group/flags. | |
| 793 | */ | |
| 794 | int changedown = 0; | |
| 795 | int changedflags = 0; | |
| 3a42736d | 796 | if (hln) |
| 44dd1628 | 797 | hltsetdino(hln, st2.st_ino); |
| 17e9c4cc MD |
798 | |
| 799 | if (RunningAsUser && (st1.st_uid != st2.st_uid || | |
| 800 | st1.st_gid != st2.st_gid)) { | |
| 801 | hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid); | |
| 802 | changedown = 1; | |
| 803 | } | |
| 804 | #ifdef _ST_FLAGS_PRESENT_ | |
| 805 | if (RunningAsUser && st1.st_flags != st2.st_flags) { | |
| 806 | hc_chflags(&DstHost, dpath, st1.st_flags); | |
| 807 | changedflags = 1; | |
| 808 | } | |
| 809 | #endif | |
| 3a42736d | 810 | if (VerboseOpt >= 3) { |
| 4d858d58 | 811 | #ifndef NOMD5 |
| 17e9c4cc MD |
812 | if (UseMD5Opt) { |
| 813 | logstd("%-32s md5-nochange", | |
| 814 | (dpath ? dpath : spath)); | |
| 815 | } else | |
| 4d858d58 | 816 | #endif |
| 17e9c4cc MD |
817 | if (UseFSMIDOpt) { |
| 818 | logstd("%-32s fsmid-nochange", | |
| 819 | (dpath ? dpath : spath)); | |
| 820 | } else if (ValidateOpt) { | |
| 821 | logstd("%-32s nochange (contents validated)", | |
| 822 | (dpath ? dpath : spath)); | |
| 823 | } else { | |
| 824 | logstd("%-32s nochange", (dpath ? dpath : spath)); | |
| 825 | } | |
| 826 | if (changedown) | |
| 827 | logstd(" (uid/gid differ)"); | |
| 828 | if (changedflags) | |
| 829 | logstd(" (flags differ)"); | |
| 830 | logstd("\n"); | |
| 3a42736d MD |
831 | } |
| 832 | CountSourceBytes += size; | |
| 833 | CountSourceItems++; | |
| a2dc574c MD |
834 | r = 0; |
| 835 | goto done; | |
| 3a42736d MD |
836 | } |
| 837 | } | |
| 838 | } | |
| 839 | if (st2Valid && !S_ISDIR(st1.st_mode) && S_ISDIR(st2.st_mode)) { | |
| 840 | if (SafetyOpt) { | |
| 841 | logerr("%-32s SAFETY - refusing to copy file over directory\n", | |
| 842 | (dpath ? dpath : spath) | |
| 843 | ); | |
| 844 | ++r; /* XXX */ | |
| a2dc574c MD |
845 | r = 0; |
| 846 | goto done; /* continue with the cpdup anyway */ | |
| 3a42736d MD |
847 | } |
| 848 | if (QuietOpt == 0 || AskConfirmation) { | |
| 849 | logstd("%-32s WARNING: non-directory source will blow away\n" | |
| 850 | "%-32s preexisting dest directory, continuing anyway!\n", | |
| 851 | ((dpath) ? dpath : spath), ""); | |
| 852 | } | |
| 853 | if (dpath) | |
| 854 | RemoveRecur(dpath, ddevNo); | |
| 855 | } | |
| 856 | ||
| 577109ea MD |
857 | /* |
| 858 | * The various comparisons failed, copy it. | |
| 859 | */ | |
| 3a42736d MD |
860 | if (S_ISDIR(st1.st_mode)) { |
| 861 | DIR *dir; | |
| 862 | ||
| 577109ea MD |
863 | if (fres < 0) |
| 864 | logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath); | |
| 4d858d58 | 865 | if ((dir = hc_opendir(&SrcHost, spath)) != NULL) { |
| 3a42736d MD |
866 | struct dirent *den; |
| 867 | int noLoop = 0; | |
| 868 | ||
| 869 | if (dpath) { | |
| 870 | if (S_ISDIR(st2.st_mode) == 0) { | |
| 77133d96 | 871 | xremove(&DstHost, dpath); |
| 4d858d58 | 872 | if (hc_mkdir(&DstHost, dpath, st1.st_mode | 0700) != 0) { |
| 3a42736d MD |
873 | logerr("%s: mkdir failed: %s\n", |
| 874 | (dpath ? dpath : spath), strerror(errno)); | |
| 875 | r = 1; | |
| 876 | noLoop = 1; | |
| 877 | } | |
| 74fa57e3 | 878 | |
| 3a42736d MD |
879 | /* |
| 880 | * Matt: why don't you check error codes here? | |
| 74fa57e3 | 881 | * (Because I'm an idiot... checks added!) |
| 3a42736d | 882 | */ |
| 74fa57e3 MD |
883 | if (hc_lstat(&DstHost, dpath, &st2) != 0) { |
| 884 | logerr("%s: lstat of newly made dir failed: %s\n", | |
| 885 | (dpath ? dpath : spath), strerror(errno)); | |
| 886 | r = 1; | |
| 887 | noLoop = 1; | |
| 888 | } | |
| 889 | if (hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid) != 0){ | |
| 890 | logerr("%s: chown of newly made dir failed: %s\n", | |
| 891 | (dpath ? dpath : spath), strerror(errno)); | |
| 892 | r = 1; | |
| 893 | noLoop = 1; | |
| 894 | } | |
| 3a42736d MD |
895 | CountCopiedItems++; |
| 896 | } else { | |
| 897 | /* | |
| 898 | * Directory must be scanable by root for cpdup to | |
| 899 | * work. We'll fix it later if the directory isn't | |
| 900 | * supposed to be readable ( which is why we fixup | |
| 901 | * st2.st_mode to match what we did ). | |
| 902 | */ | |
| 903 | if ((st2.st_mode & 0700) != 0700) { | |
| 4d858d58 | 904 | hc_chmod(&DstHost, dpath, st2.st_mode | 0700); |
| 3a42736d MD |
905 | st2.st_mode |= 0700; |
| 906 | } | |
| 907 | if (VerboseOpt >= 2) | |
| 908 | logstd("%s\n", dpath ? dpath : spath); | |
| 909 | } | |
| 910 | } | |
| 911 | ||
| 1c755102 | 912 | if ((int)sdevNo >= 0 && st1.st_dev != sdevNo) { |
| 3a42736d MD |
913 | noLoop = 1; |
| 914 | } else { | |
| 915 | sdevNo = st1.st_dev; | |
| 916 | } | |
| 917 | ||
| 1c755102 | 918 | if ((int)ddevNo >= 0 && st2.st_dev != ddevNo) { |
| 3a42736d MD |
919 | noLoop = 1; |
| 920 | } else { | |
| 921 | ddevNo = st2.st_dev; | |
| 922 | } | |
| 923 | ||
| 924 | /* | |
| 925 | * scan .cpignore file for files/directories | |
| 926 | * to ignore. | |
| 927 | */ | |
| 928 | ||
| 929 | if (UseCpFile) { | |
| 930 | FILE *fi; | |
| 975200d7 | 931 | char *buf = malloc(GETBUFSIZE); |
| 3a42736d MD |
932 | char *fpath; |
| 933 | ||
| 934 | if (UseCpFile[0] == '/') { | |
| 935 | fpath = mprintf("%s", UseCpFile); | |
| 936 | } else { | |
| 937 | fpath = mprintf("%s/%s", spath, UseCpFile); | |
| 938 | } | |
| a2dc574c | 939 | AddList(list, strrchr(fpath, '/') + 1, 1); |
| 3a42736d | 940 | if ((fi = fopen(fpath, "r")) != NULL) { |
| 975200d7 | 941 | while (fgets(buf, GETBUFSIZE, fi) != NULL) { |
| 3a42736d | 942 | int l = strlen(buf); |
| d5fdcd00 | 943 | CountSourceReadBytes += l; |
| 3a42736d MD |
944 | if (l && buf[l-1] == '\n') |
| 945 | buf[--l] = 0; | |
| c2609432 | 946 | if (buf[0]) |
| a2dc574c | 947 | AddList(list, buf, 1); |
| 3a42736d MD |
948 | } |
| 949 | fclose(fi); | |
| 950 | } | |
| 951 | free(fpath); | |
| 975200d7 | 952 | free(buf); |
| 3a42736d MD |
953 | } |
| 954 | ||
| 955 | /* | |
| 956 | * Automatically exclude MD5CacheFile that we create on the | |
| 957 | * source from the copy to the destination. | |
| 577109ea MD |
958 | * |
| 959 | * Automatically exclude a FSMIDCacheFile on the source that | |
| 960 | * would otherwise overwrite the one we maintain on the target. | |
| 3a42736d MD |
961 | */ |
| 962 | if (UseMD5Opt) | |
| a2dc574c | 963 | AddList(list, MD5CacheFile, 1); |
| 577109ea | 964 | if (UseFSMIDOpt) |
| a2dc574c | 965 | AddList(list, FSMIDCacheFile, 1); |
| 3a42736d | 966 | |
| 4d858d58 | 967 | while (noLoop == 0 && (den = hc_readdir(&SrcHost, dir)) != NULL) { |
| 3a42736d MD |
968 | /* |
| 969 | * ignore . and .. | |
| 970 | */ | |
| 971 | char *nspath; | |
| 972 | char *ndpath = NULL; | |
| 973 | ||
| 974 | if (strcmp(den->d_name, ".") == 0 || | |
| 975 | strcmp(den->d_name, "..") == 0 | |
| 976 | ) { | |
| 977 | continue; | |
| 978 | } | |
| 979 | /* | |
| 980 | * ignore if on .cpignore list | |
| 981 | */ | |
| a2dc574c | 982 | if (AddList(list, den->d_name, 0) == 1) { |
| 3a42736d MD |
983 | continue; |
| 984 | } | |
| 985 | nspath = mprintf("%s/%s", spath, den->d_name); | |
| 986 | if (dpath) | |
| 987 | ndpath = mprintf("%s/%s", dpath, den->d_name); | |
| a2dc574c MD |
988 | |
| 989 | #if USE_PTHREADS | |
| 975200d7 | 990 | if (CurParallel < MaxParallel || depth > MAXDEPTH) { |
| a2dc574c | 991 | copy_info_t cinfo = malloc(sizeof(*cinfo)); |
| 317cb9fe | 992 | pthread_t dummy_thr; |
| a2dc574c MD |
993 | |
| 994 | bzero(cinfo, sizeof(*cinfo)); | |
| 995 | cinfo->spath = nspath; | |
| 996 | cinfo->dpath = ndpath; | |
| 997 | cinfo->sdevNo = sdevNo; | |
| 998 | cinfo->ddevNo = ddevNo; | |
| 999 | cinfo->parent = info; | |
| 1000 | ++CurParallel; | |
| 1001 | ++info->children; | |
| 1002 | pthread_create(&dummy_thr, NULL, DoCopyThread, cinfo); | |
| 1003 | } else | |
| 1004 | #endif | |
| 1005 | { | |
| 1006 | info->spath = nspath; | |
| 1007 | info->dpath = ndpath; | |
| 1008 | info->sdevNo = sdevNo; | |
| 1009 | info->ddevNo = ddevNo; | |
| 975200d7 MD |
1010 | if (depth < 0) |
| 1011 | r += DoCopy(info, depth); | |
| 1012 | else | |
| 1013 | r += DoCopy(info, depth + 1); | |
| a2dc574c MD |
1014 | free(nspath); |
| 1015 | if (ndpath) | |
| 1016 | free(ndpath); | |
| 975200d7 MD |
1017 | info->spath = NULL; |
| 1018 | info->dpath = NULL; | |
| a2dc574c | 1019 | } |
| 3a42736d MD |
1020 | } |
| 1021 | ||
| 4d858d58 | 1022 | hc_closedir(&SrcHost, dir); |
| 3a42736d | 1023 | |
| a2dc574c MD |
1024 | #if USE_PTHREADS |
| 1025 | /* | |
| 1026 | * Wait for our children to finish | |
| 1027 | */ | |
| 1028 | while (info->children) { | |
| 1029 | pthread_cond_wait(&info->cond, &MasterMutex); | |
| 1030 | } | |
| 1031 | r += info->r; | |
| 1032 | info->r = 0; | |
| 1033 | #endif | |
| 1034 | ||
| 3a42736d MD |
1035 | /* |
| 1036 | * Remove files/directories from destination that do not appear | |
| 1037 | * in the source. | |
| 1038 | */ | |
| 4d858d58 MD |
1039 | if (dpath && (dir = hc_opendir(&DstHost, dpath)) != NULL) { |
| 1040 | while (noLoop == 0 && (den = hc_readdir(&DstHost, dir)) != NULL) { | |
| 3a42736d MD |
1041 | /* |
| 1042 | * ignore . or .. | |
| 1043 | */ | |
| 1044 | if (strcmp(den->d_name, ".") == 0 || | |
| 1045 | strcmp(den->d_name, "..") == 0 | |
| 1046 | ) { | |
| 1047 | continue; | |
| 1048 | } | |
| 1049 | /* | |
| 1050 | * If object does not exist in source or .cpignore | |
| 1051 | * then recursively remove it. | |
| 1052 | */ | |
| a2dc574c | 1053 | if (AddList(list, den->d_name, 3) == 3) { |
| 3a42736d MD |
1054 | char *ndpath; |
| 1055 | ||
| 1056 | ndpath = mprintf("%s/%s", dpath, den->d_name); | |
| 1057 | RemoveRecur(ndpath, ddevNo); | |
| 1058 | free(ndpath); | |
| 1059 | } | |
| 1060 | } | |
| 4d858d58 | 1061 | hc_closedir(&DstHost, dir); |
| 3a42736d MD |
1062 | } |
| 1063 | ||
| 1064 | if (dpath) { | |
| 8c2e6bdc MD |
1065 | struct timeval tv[2]; |
| 1066 | ||
| 3a42736d MD |
1067 | if (ForceOpt || |
| 1068 | st2Valid == 0 || | |
| 1069 | st1.st_uid != st2.st_uid || | |
| 1070 | st1.st_gid != st2.st_gid | |
| 1071 | ) { | |
| 4d858d58 | 1072 | hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid); |
| 3a42736d MD |
1073 | } |
| 1074 | if (st2Valid == 0 || st1.st_mode != st2.st_mode) { | |
| 4d858d58 | 1075 | hc_chmod(&DstHost, dpath, st1.st_mode); |
| 3a42736d | 1076 | } |
| 4d858d58 | 1077 | #ifdef _ST_FLAGS_PRESENT_ |
| 3a42736d | 1078 | if (st2Valid == 0 || st1.st_flags != st2.st_flags) { |
| 4d858d58 | 1079 | hc_chflags(&DstHost, dpath, st1.st_flags); |
| 3a42736d | 1080 | } |
| 4d858d58 | 1081 | #endif |
| 8c2e6bdc MD |
1082 | if (ForceOpt || |
| 1083 | st2Valid == 0 || | |
| 1084 | st1.st_mtime != st2.st_mtime | |
| 1085 | ) { | |
| 1086 | bzero(tv, sizeof(tv)); | |
| 1087 | tv[0].tv_sec = st1.st_mtime; | |
| 1088 | tv[1].tv_sec = st1.st_mtime; | |
| 1089 | hc_utimes(&DstHost, dpath, tv); | |
| 1090 | } | |
| 3a42736d MD |
1091 | } |
| 1092 | } | |
| 1093 | } else if (dpath == NULL) { | |
| 1094 | /* | |
| 1095 | * If dpath is NULL, we are just updating the MD5 | |
| 1096 | */ | |
| 4d858d58 | 1097 | #ifndef NOMD5 |
| 3a42736d MD |
1098 | if (UseMD5Opt && S_ISREG(st1.st_mode)) { |
| 1099 | mres = md5_check(spath, NULL); | |
| 1100 | ||
| 1101 | if (VerboseOpt > 1) { | |
| 1102 | if (mres < 0) | |
| 1103 | logstd("%-32s md5-update\n", (dpath) ? dpath : spath); | |
| 1104 | else | |
| 1105 | logstd("%-32s md5-ok\n", (dpath) ? dpath : spath); | |
| 1106 | } else if (!QuietOpt && mres < 0) { | |
| 1107 | logstd("%-32s md5-update\n", (dpath) ? dpath : spath); | |
| 1108 | } | |
| 1109 | } | |
| 4d858d58 | 1110 | #endif |
| 3a42736d MD |
1111 | } else if (S_ISREG(st1.st_mode)) { |
| 1112 | char *path; | |
| 4e316ad5 | 1113 | char *hpath; |
| 3a42736d MD |
1114 | int fd1; |
| 1115 | int fd2; | |
| 1116 | ||
| 0e9fad5e MD |
1117 | if (st2Valid) |
| 1118 | path = mprintf("%s.tmp%d", dpath, (int)getpid()); | |
| 1119 | else | |
| 1120 | path = mprintf("%s", dpath); | |
| 3a42736d MD |
1121 | |
| 1122 | /* | |
| 1123 | * Handle check failure message. | |
| 1124 | */ | |
| 4d858d58 | 1125 | #ifndef NOMD5 |
| 3a42736d MD |
1126 | if (mres < 0) |
| 1127 | logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath); | |
| 4d858d58 MD |
1128 | else |
| 1129 | #endif | |
| 1130 | if (fres < 0) | |
| 577109ea | 1131 | logerr("%-32s fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath); |
| 3a42736d | 1132 | |
| 4e316ad5 MD |
1133 | /* |
| 1134 | * Not quite ready to do the copy yet. If UseHLPath is defined, | |
| 1135 | * see if we can hardlink instead. | |
| 6db8f15d MD |
1136 | * |
| 1137 | * If we can hardlink, and the target exists, we have to remove it | |
| 1138 | * first or the hardlink will fail. This can occur in a number of | |
| 1139 | * situations but must typically when the '-f -H' combination is | |
| 1140 | * used. | |
| 4e316ad5 | 1141 | */ |
| 4e316ad5 | 1142 | if (UseHLPath && (hpath = checkHLPath(&st1, spath, dpath)) != NULL) { |
| 6db8f15d | 1143 | if (st2Valid) |
| 77133d96 | 1144 | xremove(&DstHost, dpath); |
| 4d858d58 | 1145 | if (hc_link(&DstHost, hpath, dpath) == 0) { |
| d5fdcd00 | 1146 | ++CountLinkedItems; |
| 4e316ad5 MD |
1147 | if (VerboseOpt) { |
| 1148 | logstd("%-32s hardlinked(-H)\n", | |
| 1149 | (dpath ? dpath : spath)); | |
| 1150 | } | |
| 1151 | free(hpath); | |
| 1152 | goto skip_copy; | |
| 1153 | } | |
| 1154 | /* | |
| 1155 | * Shucks, we may have hit a filesystem hard linking limit, | |
| 1156 | * we have to copy instead. | |
| 1157 | */ | |
| 1158 | free(hpath); | |
| 1159 | } | |
| 1160 | ||
| 4d858d58 MD |
1161 | if ((fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0)) >= 0) { |
| 1162 | if ((fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { | |
| 3a42736d MD |
1163 | /* |
| 1164 | * There could be a .tmp file from a previously interrupted | |
| 1165 | * run, delete and retry. Fail if we still can't get at it. | |
| 1166 | */ | |
| 4d858d58 MD |
1167 | #ifdef _ST_FLAGS_PRESENT_ |
| 1168 | hc_chflags(&DstHost, path, 0); | |
| 1169 | #endif | |
| 1170 | hc_remove(&DstHost, path); | |
| 1171 | fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); | |
| 3a42736d MD |
1172 | } |
| 1173 | if (fd2 >= 0) { | |
| 1c755102 | 1174 | const char *op; |
| 975200d7 | 1175 | char *iobuf1 = malloc(GETIOSIZE); |
| 3a42736d MD |
1176 | int n; |
| 1177 | ||
| 1178 | /* | |
| 1179 | * Matt: What about holes? | |
| 1180 | */ | |
| 1181 | op = "read"; | |
| 975200d7 | 1182 | while ((n = hc_read(&SrcHost, fd1, iobuf1, GETIOSIZE)) > 0) { |
| 3a42736d | 1183 | op = "write"; |
| 975200d7 | 1184 | if (hc_write(&DstHost, fd2, iobuf1, n) != n) |
| 3a42736d MD |
1185 | break; |
| 1186 | op = "read"; | |
| 1187 | } | |
| 4d858d58 | 1188 | hc_close(&DstHost, fd2); |
| 3a42736d MD |
1189 | if (n == 0) { |
| 1190 | struct timeval tv[2]; | |
| 1191 | ||
| 1192 | bzero(tv, sizeof(tv)); | |
| 1193 | tv[0].tv_sec = st1.st_mtime; | |
| 1194 | tv[1].tv_sec = st1.st_mtime; | |
| 1195 | ||
| 4d858d58 MD |
1196 | hc_chown(&DstHost, path, st1.st_uid, st1.st_gid); |
| 1197 | hc_chmod(&DstHost, path, st1.st_mode); | |
| 17e9c4cc MD |
1198 | #ifdef _ST_FLAGS_PRESENT_ |
| 1199 | if (st1.st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) | |
| 1200 | hc_utimes(&DstHost, path, tv); | |
| 1201 | #else | |
| 1202 | hc_utimes(&DstHost, path, tv); | |
| 1203 | #endif | |
| 0e9fad5e | 1204 | if (st2Valid && xrename(path, dpath, st2_flags) != 0) { |
| 3a42736d MD |
1205 | logerr("%-32s rename-after-copy failed: %s\n", |
| 1206 | (dpath ? dpath : spath), strerror(errno) | |
| 1207 | ); | |
| 1208 | ++r; | |
| 1209 | } else { | |
| 1210 | if (VerboseOpt) | |
| 1211 | logstd("%-32s copy-ok\n", (dpath ? dpath : spath)); | |
| 4d858d58 | 1212 | #ifdef _ST_FLAGS_PRESENT_ |
| 3a42736d | 1213 | if (st1.st_flags) |
| 4d858d58 MD |
1214 | hc_chflags(&DstHost, dpath, st1.st_flags); |
| 1215 | #endif | |
| 3a42736d | 1216 | } |
| 17e9c4cc MD |
1217 | #ifdef _ST_FLAGS_PRESENT_ |
| 1218 | if ((st1.st_flags & (UF_IMMUTABLE|SF_IMMUTABLE)) == 0) | |
| 1219 | hc_utimes(&DstHost, dpath, tv); | |
| 1220 | #endif | |
| d5fdcd00 | 1221 | CountSourceReadBytes += size; |
| 3a42736d MD |
1222 | CountWriteBytes += size; |
| 1223 | CountSourceBytes += size; | |
| 1224 | CountSourceItems++; | |
| 1225 | CountCopiedItems++; | |
| 1226 | } else { | |
| 1227 | logerr("%-32s %s failed: %s\n", | |
| 1228 | (dpath ? dpath : spath), op, strerror(errno) | |
| 1229 | ); | |
| 4d858d58 | 1230 | hc_remove(&DstHost, path); |
| 3a42736d MD |
1231 | ++r; |
| 1232 | } | |
| 975200d7 | 1233 | free(iobuf1); |
| 3a42736d MD |
1234 | } else { |
| 1235 | logerr("%-32s create (uid %d, euid %d) failed: %s\n", | |
| 1236 | (dpath ? dpath : spath), getuid(), geteuid(), | |
| 1237 | strerror(errno) | |
| 1238 | ); | |
| 1239 | ++r; | |
| 1240 | } | |
| 4d858d58 | 1241 | hc_close(&SrcHost, fd1); |
| 3a42736d MD |
1242 | } else { |
| 1243 | logerr("%-32s copy: open failed: %s\n", | |
| 1244 | (dpath ? dpath : spath), | |
| 1245 | strerror(errno) | |
| 1246 | ); | |
| 1247 | ++r; | |
| 1248 | } | |
| 4e316ad5 | 1249 | skip_copy: |
| 3a42736d MD |
1250 | free(path); |
| 1251 | ||
| 1252 | if (hln) { | |
| 44dd1628 MD |
1253 | if (!r && hc_stat(&DstHost, dpath, &st2) == 0) { |
| 1254 | hltsetdino(hln, st2.st_ino); | |
| 1255 | } else { | |
| 3a42736d | 1256 | hltdelete(hln); |
| 44dd1628 MD |
1257 | hln = NULL; |
| 1258 | } | |
| 3a42736d MD |
1259 | } |
| 1260 | } else if (S_ISLNK(st1.st_mode)) { | |
| 975200d7 MD |
1261 | char *link1 = malloc(GETLINKSIZE); |
| 1262 | char *link2 = malloc(GETLINKSIZE); | |
| 0e9fad5e | 1263 | char *path; |
| 3a42736d MD |
1264 | int n1; |
| 1265 | int n2; | |
| 1266 | ||
| 975200d7 | 1267 | n1 = hc_readlink(&SrcHost, spath, link1, GETLINKSIZE - 1); |
| 0e9fad5e MD |
1268 | if (st2Valid) { |
| 1269 | path = mprintf("%s.tmp%d", dpath, (int)getpid()); | |
| 1270 | n2 = hc_readlink(&DstHost, dpath, link2, GETLINKSIZE - 1); | |
| 1271 | } else { | |
| 1272 | path = mprintf("%s", dpath); | |
| 1273 | n2 = -1; | |
| 1274 | } | |
| 3a42736d MD |
1275 | if (n1 >= 0) { |
| 1276 | if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) { | |
| 4d858d58 | 1277 | hc_umask(&DstHost, ~st1.st_mode); |
| 77133d96 | 1278 | xremove(&DstHost, path); |
| 3a42736d | 1279 | link1[n1] = 0; |
| 4d858d58 | 1280 | if (hc_symlink(&DstHost, link1, path) < 0) { |
| 3a42736d MD |
1281 | logerr("%-32s symlink (%s->%s) failed: %s\n", |
| 1282 | (dpath ? dpath : spath), link1, path, | |
| 1283 | strerror(errno) | |
| 1284 | ); | |
| 1285 | ++r; | |
| 1286 | } else { | |
| 4d858d58 | 1287 | hc_lchown(&DstHost, path, st1.st_uid, st1.st_gid); |
| 3a42736d MD |
1288 | /* |
| 1289 | * there is no lchmod() or lchflags(), we | |
| 1290 | * cannot chmod or chflags a softlink. | |
| 1291 | */ | |
| 0e9fad5e | 1292 | if (st2Valid && xrename(path, dpath, st2_flags) != 0) { |
| 3a42736d MD |
1293 | logerr("%-32s rename softlink (%s->%s) failed: %s\n", |
| 1294 | (dpath ? dpath : spath), | |
| 1295 | path, dpath, strerror(errno)); | |
| 1296 | } else if (VerboseOpt) { | |
| 1297 | logstd("%-32s softlink-ok\n", (dpath ? dpath : spath)); | |
| 1298 | } | |
| 4d858d58 | 1299 | hc_umask(&DstHost, 000); |
| 3a42736d MD |
1300 | CountWriteBytes += n1; |
| 1301 | CountCopiedItems++; | |
| 1302 | } | |
| 1303 | } else { | |
| 1304 | if (VerboseOpt >= 3) | |
| 1305 | logstd("%-32s nochange\n", (dpath ? dpath : spath)); | |
| 1306 | } | |
| 1307 | CountSourceBytes += n1; | |
| d5fdcd00 MD |
1308 | CountSourceReadBytes += n1; |
| 1309 | if (n2 > 0) | |
| 1310 | CountTargetReadBytes += n2; | |
| 3a42736d MD |
1311 | CountSourceItems++; |
| 1312 | } else { | |
| 1313 | r = 1; | |
| 1314 | logerr("%-32s softlink-failed\n", (dpath ? dpath : spath)); | |
| 1315 | } | |
| 975200d7 MD |
1316 | free(link1); |
| 1317 | free(link2); | |
| 1318 | free(path); | |
| ac482a7e | 1319 | } else if ((S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) && DeviceOpt) { |
| 0e9fad5e | 1320 | char *path = NULL; |
| 3a42736d MD |
1321 | |
| 1322 | if (ForceOpt || | |
| 1323 | st2Valid == 0 || | |
| 1324 | st1.st_mode != st2.st_mode || | |
| 1325 | st1.st_rdev != st2.st_rdev || | |
| 1326 | st1.st_uid != st2.st_uid || | |
| 1327 | st1.st_gid != st2.st_gid | |
| 1328 | ) { | |
| 0e9fad5e MD |
1329 | if (st2Valid) { |
| 1330 | path = mprintf("%s.tmp%d", dpath, (int)getpid()); | |
| 1331 | xremove(&DstHost, path); | |
| 1332 | } else { | |
| 1333 | path = mprintf("%s", dpath); | |
| 1334 | } | |
| 3a42736d | 1335 | |
| 975200d7 | 1336 | if (hc_mknod(&DstHost, path, st1.st_mode, st1.st_rdev) == 0) { |
| 4d858d58 MD |
1337 | hc_chmod(&DstHost, path, st1.st_mode); |
| 1338 | hc_chown(&DstHost, path, st1.st_uid, st1.st_gid); | |
| 0e9fad5e MD |
1339 | if (st2Valid) |
| 1340 | xremove(&DstHost, dpath); | |
| 1341 | if (st2Valid && xrename(path, dpath, st2_flags) != 0) { | |
| 3a42736d MD |
1342 | logerr("%-32s dev-rename-after-create failed: %s\n", |
| 1343 | (dpath ? dpath : spath), | |
| 1344 | strerror(errno) | |
| 1345 | ); | |
| 1346 | } else if (VerboseOpt) { | |
| 1347 | logstd("%-32s dev-ok\n", (dpath ? dpath : spath)); | |
| 1348 | } | |
| 1349 | CountCopiedItems++; | |
| 1350 | } else { | |
| 1351 | r = 1; | |
| 1352 | logerr("%-32s dev failed: %s\n", | |
| 1353 | (dpath ? dpath : spath), strerror(errno) | |
| 1354 | ); | |
| 1355 | } | |
| 1356 | } else { | |
| 1357 | if (VerboseOpt >= 3) | |
| 1358 | logstd("%-32s nochange\n", (dpath ? dpath : spath)); | |
| 1359 | } | |
| 0e9fad5e MD |
1360 | if (path) |
| 1361 | free(path); | |
| 3a42736d MD |
1362 | CountSourceItems++; |
| 1363 | } | |
| a2dc574c | 1364 | done: |
| 975200d7 MD |
1365 | if (hln) { |
| 1366 | if (hln->dino == (ino_t)-1) { | |
| 1367 | hltdelete(hln); | |
| 1368 | /*hln = NULL; unneeded */ | |
| 1369 | } else { | |
| 1370 | hltrels(hln); | |
| 1371 | } | |
| 1372 | } | |
| a2dc574c MD |
1373 | ResetList(list); |
| 1374 | free(list); | |
| 577109ea | 1375 | return (r); |
| 3a42736d MD |
1376 | } |
| 1377 | ||
| 1378 | /* | |
| 1379 | * RemoveRecur() | |
| 1380 | */ | |
| 1381 | ||
| 1382 | void | |
| 1c755102 | 1383 | RemoveRecur(const char *dpath, dev_t devNo) |
| 3a42736d MD |
1384 | { |
| 1385 | struct stat st; | |
| 1386 | ||
| 4d858d58 | 1387 | if (hc_lstat(&DstHost, dpath, &st) == 0) { |
| 1c755102 | 1388 | if ((int)devNo < 0) |
| 3a42736d MD |
1389 | devNo = st.st_dev; |
| 1390 | if (st.st_dev == devNo) { | |
| 1391 | if (S_ISDIR(st.st_mode)) { | |
| 1392 | DIR *dir; | |
| 1393 | ||
| 4d858d58 | 1394 | if ((dir = hc_opendir(&DstHost, dpath)) != NULL) { |
| 3a42736d | 1395 | struct dirent *den; |
| 4d858d58 | 1396 | while ((den = hc_readdir(&DstHost, dir)) != NULL) { |
| 3a42736d MD |
1397 | char *ndpath; |
| 1398 | ||
| 1399 | if (strcmp(den->d_name, ".") == 0) | |
| 1400 | continue; | |
| 1401 | if (strcmp(den->d_name, "..") == 0) | |
| 1402 | continue; | |
| 1403 | ndpath = mprintf("%s/%s", dpath, den->d_name); | |
| 1404 | RemoveRecur(ndpath, devNo); | |
| 1405 | free(ndpath); | |
| 1406 | } | |
| 4d858d58 | 1407 | hc_closedir(&DstHost, dir); |
| 3a42736d | 1408 | } |
| c0d18d1d | 1409 | if (AskConfirmation && NoRemoveOpt == 0) { |
| 3a42736d | 1410 | if (YesNo(dpath)) { |
| 4d858d58 | 1411 | if (hc_rmdir(&DstHost, dpath) < 0) { |
| 3a42736d MD |
1412 | logerr("%-32s rmdir failed: %s\n", |
| 1413 | dpath, strerror(errno) | |
| 1414 | ); | |
| 1415 | } | |
| 1416 | CountRemovedItems++; | |
| 1417 | } | |
| 1418 | } else { | |
| 1419 | if (NoRemoveOpt) { | |
| 1420 | if (VerboseOpt) | |
| 1421 | logstd("%-32s not-removed\n", dpath); | |
| 4d858d58 | 1422 | } else if (hc_rmdir(&DstHost, dpath) == 0) { |
| 3a42736d MD |
1423 | if (VerboseOpt) |
| 1424 | logstd("%-32s rmdir-ok\n", dpath); | |
| 1425 | CountRemovedItems++; | |
| 1426 | } else { | |
| 1427 | logerr("%-32s rmdir failed: %s\n", | |
| 1428 | dpath, strerror(errno) | |
| 1429 | ); | |
| 1430 | } | |
| 1431 | } | |
| 1432 | } else { | |
| c0d18d1d | 1433 | if (AskConfirmation && NoRemoveOpt == 0) { |
| 3a42736d | 1434 | if (YesNo(dpath)) { |
| 77133d96 | 1435 | if (xremove(&DstHost, dpath) < 0) { |
| 3a42736d MD |
1436 | logerr("%-32s remove failed: %s\n", |
| 1437 | dpath, strerror(errno) | |
| 1438 | ); | |
| 1439 | } | |
| 1440 | CountRemovedItems++; | |
| 1441 | } | |
| 1442 | } else { | |
| 1443 | if (NoRemoveOpt) { | |
| 1444 | if (VerboseOpt) | |
| 1445 | logstd("%-32s not-removed\n", dpath); | |
| 77133d96 | 1446 | } else if (xremove(&DstHost, dpath) == 0) { |
| 3a42736d MD |
1447 | if (VerboseOpt) |
| 1448 | logstd("%-32s remove-ok\n", dpath); | |
| 1449 | CountRemovedItems++; | |
| 1450 | } else { | |
| 1451 | logerr("%-32s remove failed: %s\n", | |
| 1452 | dpath, strerror(errno) | |
| 1453 | ); | |
| 1454 | } | |
| 1455 | } | |
| 1456 | } | |
| 1457 | } | |
| 1458 | } | |
| 1459 | } | |
| 1460 | ||
| 1461 | void | |
| 1462 | InitList(List *list) | |
| 1463 | { | |
| 1464 | bzero(list, sizeof(List)); | |
| 1465 | list->li_Node.no_Next = &list->li_Node; | |
| 1466 | } | |
| 1467 | ||
| 1468 | void | |
| 1469 | ResetList(List *list) | |
| 1470 | { | |
| 1471 | Node *node; | |
| 1472 | ||
| 1473 | while ((node = list->li_Node.no_Next) != &list->li_Node) { | |
| 1474 | list->li_Node.no_Next = node->no_Next; | |
| 1475 | free(node); | |
| 1476 | } | |
| 1477 | InitList(list); | |
| 1478 | } | |
| 1479 | ||
| 1480 | int | |
| 1481 | AddList(List *list, const char *name, int n) | |
| 1482 | { | |
| 1483 | Node *node; | |
| 58860d7d MD |
1484 | int hv; |
| 1485 | ||
| 1486 | hv = shash(name); | |
| 3a42736d MD |
1487 | |
| 1488 | /* | |
| 1489 | * Scan against wildcards. Only a node value of 1 can be a wildcard | |
| 1490 | * ( usually scanned from .cpignore ) | |
| 1491 | */ | |
| 1492 | ||
| 1493 | for (node = list->li_Hash[0]; node; node = node->no_HNext) { | |
| 1494 | if (strcmp(name, node->no_Name) == 0 || | |
| 1495 | (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0) | |
| 1496 | ) { | |
| 1497 | return(node->no_Value); | |
| 1498 | } | |
| 1499 | } | |
| 1500 | ||
| 1501 | /* | |
| 1502 | * Look for exact match | |
| 1503 | */ | |
| 1504 | ||
| 1505 | for (node = list->li_Hash[hv]; node; node = node->no_HNext) { | |
| 1506 | if (strcmp(name, node->no_Name) == 0) { | |
| 1507 | return(node->no_Value); | |
| 1508 | } | |
| 1509 | } | |
| 1510 | node = malloc(sizeof(Node) + strlen(name) + 1); | |
| 58860d7d MD |
1511 | if (node == NULL) { |
| 1512 | fprintf(stderr, "out of memory\n"); | |
| 1513 | exit(EXIT_FAILURE); | |
| 1514 | } | |
| 3a42736d MD |
1515 | |
| 1516 | node->no_Next = list->li_Node.no_Next; | |
| 1517 | list->li_Node.no_Next = node; | |
| 1518 | ||
| 1519 | node->no_HNext = list->li_Hash[hv]; | |
| 1520 | list->li_Hash[hv] = node; | |
| 1521 | ||
| 1522 | strcpy(node->no_Name, name); | |
| 1523 | node->no_Value = n; | |
| 1524 | ||
| 1525 | return(n); | |
| 1526 | } | |
| 1527 | ||
| fce2564b | 1528 | static int |
| 3a42736d MD |
1529 | shash(const char *s) |
| 1530 | { | |
| 58860d7d MD |
1531 | int hv; |
| 1532 | ||
| 1533 | hv = 0xA4FB3255; | |
| 3a42736d MD |
1534 | |
| 1535 | while (*s) { | |
| 1536 | if (*s == '*' || *s == '?' || | |
| 1537 | *s == '{' || *s == '}' || | |
| 1538 | *s == '[' || *s == ']' || | |
| 1539 | *s == '|' | |
| 1540 | ) { | |
| 1541 | return(0); | |
| 1542 | } | |
| 1543 | hv = (hv << 5) ^ *s ^ (hv >> 23); | |
| 1544 | ++s; | |
| 1545 | } | |
| 1546 | return(((hv >> 16) ^ hv) & HMASK); | |
| 1547 | } | |
| 1548 | ||
| 1549 | /* | |
| 1550 | * WildCmp() - compare wild string to sane string | |
| 1551 | * | |
| 1552 | * Return 0 on success, -1 on failure. | |
| 1553 | */ | |
| 1554 | ||
| 1555 | int | |
| 1556 | WildCmp(const char *w, const char *s) | |
| 1557 | { | |
| 1558 | /* | |
| 1559 | * skip fixed portion | |
| 1560 | */ | |
| 1561 | ||
| 1562 | for (;;) { | |
| 1563 | switch(*w) { | |
| 1564 | case '*': | |
| 1565 | if (w[1] == 0) /* optimize wild* case */ | |
| 1566 | return(0); | |
| 1567 | { | |
| 1568 | int i; | |
| 1569 | int l = strlen(s); | |
| 1570 | ||
| 1571 | for (i = 0; i <= l; ++i) { | |
| 1572 | if (WildCmp(w + 1, s + i) == 0) | |
| 1573 | return(0); | |
| 1574 | } | |
| 1575 | } | |
| 1576 | return(-1); | |
| 1577 | case '?': | |
| 1578 | if (*s == 0) | |
| 1579 | return(-1); | |
| 1580 | ++w; | |
| 1581 | ++s; | |
| 1582 | break; | |
| 1583 | default: | |
| 1584 | if (*w != *s) | |
| 1585 | return(-1); | |
| 1586 | if (*w == 0) /* terminator */ | |
| 1587 | return(0); | |
| 1588 | ++w; | |
| 1589 | ++s; | |
| 1590 | break; | |
| 1591 | } | |
| 1592 | } | |
| 1593 | /* not reached */ | |
| 1594 | return(-1); | |
| 1595 | } | |
| 1596 | ||
| 1597 | int | |
| 1598 | YesNo(const char *path) | |
| 1599 | { | |
| 1600 | int ch, first; | |
| 1601 | ||
| 58860d7d MD |
1602 | fprintf(stderr, "remove %s (Yes/No) [No]? ", path); |
| 1603 | fflush(stderr); | |
| 3a42736d MD |
1604 | |
| 1605 | first = ch = getchar(); | |
| 1606 | while (ch != '\n' && ch != EOF) | |
| 1607 | ch = getchar(); | |
| 1608 | return ((first == 'y' || first == 'Y')); | |
| 1609 | } | |
| 1610 | ||
| 3a42736d MD |
1611 | /* |
| 1612 | * xrename() - rename with override | |
| 1613 | * | |
| 1614 | * If the rename fails, attempt to override st_flags on the | |
| 1615 | * destination and rename again. If that fails too, try to | |
| 1616 | * set the flags back the way they were and give up. | |
| 1617 | */ | |
| 1618 | ||
| fce2564b | 1619 | static int |
| 3a42736d MD |
1620 | xrename(const char *src, const char *dst, u_long flags) |
| 1621 | { | |
| 58860d7d MD |
1622 | int r; |
| 1623 | ||
| 1624 | r = 0; | |
| 3a42736d | 1625 | |
| 4d858d58 MD |
1626 | if ((r = hc_rename(&DstHost, src, dst)) < 0) { |
| 1627 | #ifdef _ST_FLAGS_PRESENT_ | |
| 1628 | hc_chflags(&DstHost, dst, 0); | |
| 1629 | if ((r = hc_rename(&DstHost, src, dst)) < 0) | |
| 1630 | hc_chflags(&DstHost, dst, flags); | |
| 1631 | #endif | |
| 3a42736d MD |
1632 | } |
| 1633 | return(r); | |
| 1634 | } | |
| 1635 | ||
| fce2564b | 1636 | static int |
| 3a42736d MD |
1637 | xlink(const char *src, const char *dst, u_long flags) |
| 1638 | { | |
| d8bce52d MD |
1639 | int r; |
| 1640 | #ifdef _ST_FLAGS_PRESENT_ | |
| 1641 | int e; | |
| 1642 | #endif | |
| 58860d7d MD |
1643 | |
| 1644 | r = 0; | |
| 3a42736d | 1645 | |
| 4d858d58 MD |
1646 | if ((r = hc_link(&DstHost, src, dst)) < 0) { |
| 1647 | #ifdef _ST_FLAGS_PRESENT_ | |
| 1648 | hc_chflags(&DstHost, src, 0); | |
| 1649 | r = hc_link(&DstHost, src, dst); | |
| 3a42736d | 1650 | e = errno; |
| 4d858d58 | 1651 | hc_chflags(&DstHost, src, flags); |
| 3a42736d | 1652 | errno = e; |
| 4d858d58 | 1653 | #endif |
| 3a42736d | 1654 | } |
| d5fdcd00 MD |
1655 | if (r == 0) |
| 1656 | ++CountLinkedItems; | |
| 3a42736d MD |
1657 | return(r); |
| 1658 | } | |
| 1659 | ||
| 77133d96 MD |
1660 | static int |
| 1661 | xremove(struct HostConf *host, const char *path) | |
| 1662 | { | |
| 1663 | int res; | |
| 1664 | ||
| 1665 | res = hc_remove(host, path); | |
| 1666 | #ifdef _ST_FLAGS_PRESENT_ | |
| 1667 | if (res == -EPERM) { | |
| 1668 | hc_chflags(host, path, 0); | |
| 1669 | res = hc_remove(host, path); | |
| 1670 | } | |
| 1671 | #endif | |
| 1672 | return(res); | |
| 1673 | } | |
| 1674 |