dosync.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /* $TOG: dosync.c /main/6 1998/10/26 12:40:53 mgreess $ */
  24. /************************************<+>*************************************
  25. ****************************************************************************
  26. *
  27. * FILE: dosync.c
  28. *
  29. *
  30. * DESCRIPTION: Routines synchronizing directories
  31. *
  32. * FUNCTIONS: CONFIRM
  33. * CmpPred
  34. * DTYPE
  35. * DirCmp
  36. * DirCmp2
  37. * DumpDir
  38. * ERROR_CHECK
  39. * FreeDir
  40. * GetDir
  41. * GetDirEntry
  42. * SortDir
  43. * SyncDirRecur
  44. * SyncDirectory
  45. * SyncItem
  46. * cmpSymlink
  47. * dirEraseCallback
  48. * dirErrorCallback
  49. * doCopy
  50. * doCopyLink
  51. * doMkdir
  52. * doSymlink
  53. * doUnlink
  54. * targetLink
  55. *
  56. * (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
  57. * (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
  58. * (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
  59. * (c) Copyright 1993, 1994, 1995 Novell, Inc.
  60. *
  61. ****************************************************************************
  62. ************************************<+>*************************************/
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <unistd.h>
  66. #include <string.h>
  67. #include <errno.h>
  68. #include <assert.h>
  69. #include <sys/types.h>
  70. #include <sys/stat.h>
  71. #include <fcntl.h>
  72. #include <sys/time.h>
  73. #include <dirent.h>
  74. #include <sys/file.h>
  75. #include "fsrtns.h"
  76. #ifdef PATRTNS
  77. #include "patrtns.h"
  78. #endif
  79. #include "dosync.h"
  80. /*--------------------------------------------------------------------
  81. * global variables
  82. *------------------------------------------------------------------*/
  83. char *FileOpNames[] = {
  84. "synchronize",
  85. "opendir",
  86. "lstat",
  87. "stat",
  88. "readlink",
  89. "delete",
  90. "copy",
  91. "mkdir",
  92. "copy link",
  93. "make link",
  94. "working",
  95. "is a copy",
  96. "is a link"
  97. };
  98. int (*syncConfirmCallback)(FileOp op,
  99. char *sname, int stype,
  100. char *tname, int ttype,
  101. char *link) = NULL;
  102. int (*syncErrorCallback)(FileOp op, char *fname, int errnum) = NULL;
  103. static SyncParams *SP;
  104. /*--------------------------------------------------------------------
  105. * macros for invoking callback functions
  106. *------------------------------------------------------------------*/
  107. #define DTYPE(delP) ((delP)? (delP)->ftype: ft_noent)
  108. #define CONFIRM(op, sn, st, tn, tt, l, rc) \
  109. if (periodicCallback && (*periodicCallback)() != 0) return -1; \
  110. else if (syncConfirmCallback && \
  111. (rc = syncConfirmCallback(op, sn, st, tn, tt, l)) != 0) return rc; else
  112. #define ERROR_CHECK(op, f, rc) \
  113. if (rc < 0 || rc && syncErrorCallback && syncErrorCallback(op, f, rc) < 0) \
  114. return -1; else
  115. /*--------------------------------------------------------------------
  116. * reading directories
  117. *------------------------------------------------------------------*/
  118. /* structure containing information anout a directory entry */
  119. typedef struct de {
  120. char name[256]; /* file name */
  121. int rc, lrc; /* return codes from stat, lstat */
  122. struct stat stat; /* stat */
  123. int ftype; /* file type flags (see #defines in dosync.h) */
  124. char exclude, skip; /* exclude, skip flags */
  125. char *link; /* value of symbolic link */
  126. struct de *teP; /* source dir: points to correspond. target dir entry */
  127. struct de *next; /* next directory entry */
  128. } DirEntry;
  129. /*
  130. * DumpDir: dump linked list of directory entries to stdout (debugging only)
  131. */
  132. static void
  133. DumpDir(DirEntry *dP)
  134. {
  135. DirEntry *eP;
  136. for (eP = dP; eP; eP = eP->next)
  137. printf(" %-15s %2d %c %c %c %c %c %s\n",
  138. eP->name,
  139. eP->rc,
  140. (eP->ftype & ft_isdir)? 'D': ' ',
  141. (eP->ftype & ft_islnk)? 'L': ' ',
  142. eP->exclude? 'X': ' ',
  143. eP->skip? 'S': ' ',
  144. eP->teP? 'F': ' ',
  145. eP->link? eP->link: "");
  146. }
  147. /*
  148. * DirCmp: compare function for sorting directory entries
  149. */
  150. static int
  151. DirCmp(const void *p1, const void *p2)
  152. {
  153. return strcmp((*(DirEntry **)p1)->name, (*(DirEntry **)p2)->name);
  154. }
  155. /*
  156. * SortDir: sort linked list of directory entries by name
  157. */
  158. static DirEntry *
  159. SortDir(DirEntry *dP, int (*cmp)(const void*, const void*))
  160. {
  161. int i, n;
  162. DirEntry *eP, **linkPP;
  163. DirEntry *da[1024], **daP;
  164. /* count number of directory entries */
  165. n = 0;
  166. for (eP = dP; eP; eP = eP->next)
  167. n++;
  168. if (n < 2)
  169. return dP;
  170. /* allocate pointer array */
  171. if (n <= 1024)
  172. daP = da;
  173. else
  174. daP = (DirEntry **)malloc(n * sizeof(DirEntry *));
  175. /* sort pointer array */
  176. for (eP = dP, i = 0; eP; eP = eP->next, i++)
  177. daP[i] = eP;
  178. qsort(daP, n, sizeof(DirEntry *), cmp);
  179. /* re-link the linked list */
  180. linkPP = &dP;
  181. for (i = 0; i < n; i++) {
  182. *linkPP = daP[i];
  183. linkPP = &(*linkPP)->next;
  184. }
  185. *linkPP = NULL;
  186. /* free pointer array */
  187. if (daP != da)
  188. free(daP);
  189. return dP;
  190. }
  191. /*
  192. * FreeDir: de-allocate linked list of directory entries
  193. */
  194. static void
  195. FreeDir(DirEntry *deP)
  196. {
  197. DirEntry *nextP;
  198. while (deP) {
  199. nextP = deP->next;
  200. free(deP);
  201. deP = nextP;
  202. }
  203. }
  204. /*
  205. * GetDirEntry: Get information about one directory entry.
  206. * If an error occurs, returns the errno in *rc and the operation
  207. * that failed (op_lstat, op_stat, or op_readlink) in *op.
  208. */
  209. static DirEntry *
  210. GetDirEntry(char *fname, FileOp *op, int *rc)
  211. {
  212. DirEntry *deP;
  213. char *p;
  214. char link[1024];
  215. int l;
  216. /* allocate new DirEntry */
  217. deP = (DirEntry *)malloc(sizeof(DirEntry));
  218. memset(deP, 0, sizeof(DirEntry));
  219. /* get file name */
  220. p = strrchr(fname, '/');
  221. if (p && p > fname)
  222. snprintf(deP->name, sizeof(deP->name), "%s", p + 1);
  223. else
  224. snprintf(deP->name, sizeof(deP->name), "%s", fname);
  225. /* assume everything is fine */
  226. *op = 0;
  227. *rc = 0;
  228. /* get information */
  229. deP->rc = lstat(fname, &deP->stat);
  230. if (deP->rc) {
  231. *op = op_lstat;
  232. *rc = errno;
  233. } else if ((deP->stat.st_mode & S_IFMT) == S_IFDIR)
  234. deP->ftype |= ft_isdir;
  235. else if ((deP->stat.st_mode & S_IFMT) == S_IFLNK) {
  236. deP->ftype |= ft_islnk;
  237. deP->lrc = stat(fname, &deP->stat);
  238. if (deP->lrc) {
  239. if (errno != ENOENT) {
  240. *op = op_stat;
  241. *rc = errno;
  242. }
  243. } else if ((deP->stat.st_mode & S_IFMT) == S_IFDIR)
  244. deP->ftype |= ft_isdir;
  245. l = readlink(fname, link, sizeof(link) - 1);
  246. if (l < 0) {
  247. *op = op_readlink;
  248. *rc = errno;
  249. } else {
  250. link[l] = 0;
  251. deP->link = strdup(link);
  252. }
  253. }
  254. return deP;
  255. }
  256. /*
  257. * GetDir: read a directory and return linked list of directory entries
  258. */
  259. static int
  260. GetDir(char *dirname, PatternList *xl, PatternList *sl, DirEntry **listPP)
  261. {
  262. DIR *dirP = NULL; /* open directory */
  263. struct dirent *entryP; /* directory entry */
  264. DirEntry *deP, *firstP, **linkPP;
  265. char fname[1024], *fnP;
  266. PatternList *pl;
  267. FileOp op;
  268. int rc;
  269. /* open directory */
  270. dirP = opendir(dirname);
  271. if (dirP == NULL) {
  272. rc = errno;
  273. if (syncErrorCallback &&
  274. syncErrorCallback(op_opendir, dirname, rc) < 0)
  275. {
  276. return -1;
  277. }
  278. return rc;
  279. }
  280. /* copy dirname to file name buffer */
  281. snprintf(fname, sizeof(fname), "%s", dirname);
  282. fnP = fname + strlen(fname);
  283. *fnP++ = '/';
  284. /* initialize linked list */
  285. firstP = NULL;
  286. linkPP = &firstP;
  287. /* read the directory */
  288. while ((entryP = readdir(dirP)) != NULL) {
  289. /* skip . and .. */
  290. if (strcmp(entryP->d_name, ".") == 0 || strcmp(entryP->d_name, "..") == 0)
  291. continue;
  292. /* get new DirEntry */
  293. strcpy(fnP, entryP->d_name);
  294. deP = GetDirEntry(fname, &op, &rc);
  295. /* add to linked list */
  296. *linkPP = deP;
  297. linkPP = &deP->next;
  298. /* if error occurred, call error callback function */
  299. if (rc != 0 && syncErrorCallback && syncErrorCallback(op, fname, rc) < 0) {
  300. closedir(dirP);
  301. FreeDir(firstP);
  302. return -1;
  303. }
  304. /* check exclude and skip list */
  305. #ifdef PATRTNS
  306. for (pl = xl ; pl; pl = pl->next)
  307. if (MatchPattern(pl->pattern, fname))
  308. deP->exclude++;
  309. for (pl = sl; pl; pl = pl->next)
  310. if (MatchPattern(pl->pattern, fname))
  311. deP->skip++;
  312. #endif
  313. }
  314. closedir(dirP);
  315. *listPP = SortDir(firstP, DirCmp);
  316. return 0;
  317. }
  318. /*--------------------------------------------------------------------
  319. * file operations
  320. *------------------------------------------------------------------*/
  321. static int erase_rc;
  322. /*
  323. * dirEraseCallback: used in doUnlink (below) for confirmation
  324. * during recursive directory deletes
  325. */
  326. static int
  327. dirEraseCallback(char *fname)
  328. {
  329. erase_rc = syncConfirmCallback(op_delete, "", ft_noent, fname, 0, "");
  330. return erase_rc;
  331. }
  332. static int
  333. dirErrorCallback(char *fname, int errnum)
  334. {
  335. erase_rc = syncErrorCallback(op_delete, fname, errnum);
  336. return erase_rc;
  337. }
  338. /*
  339. * doUnlink: delete a file from the target directory
  340. */
  341. static int
  342. doUnlink(char *fname, DirEntry *fP, int confirm)
  343. {
  344. int rc;
  345. if (confirm)
  346. CONFIRM(op_delete, "", ft_noent, fname, fP->ftype, "", rc);
  347. if (SP->dontdoit)
  348. rc = 0;
  349. else if (SP->keepold) {
  350. char newname[1024];
  351. snprintf(newname, sizeof(newname), "%s%s", fname, SP->keepold);
  352. fsMove(fname, newname, 1, &rc);
  353. } else if ((fP->ftype & ft_isdir) && !(fP->ftype & ft_islnk)) {
  354. /* arrange for syncConfirmCallback's during recursive directory deletes */
  355. erase_rc = 0;
  356. if (syncErrorCallback)
  357. errorCallback = dirErrorCallback;
  358. if (syncConfirmCallback)
  359. progressCallback = dirEraseCallback;
  360. fsErase(fname, &rc, 1);
  361. if (rc < 0 && erase_rc)
  362. rc = ENOTEMPTY;
  363. errorCallback = NULL;
  364. progressCallback = NULL;
  365. } else
  366. rc = (unlink(fname) != 0)? errno: 0;
  367. /* check for errors */
  368. ERROR_CHECK(op_delete, fname, rc);
  369. return rc;
  370. }
  371. /*
  372. * doCopy: copy a file from the source to target directory
  373. */
  374. static int
  375. doCopy(char *sname, DirEntry *sP, char *tname, DirEntry *delP)
  376. {
  377. int rc;
  378. /* display message */
  379. CONFIRM(op_copy, sname, sP->ftype, tname, DTYPE(delP), "", rc);
  380. /* if necessary, delete target */
  381. if (delP && !SP->dontdoit)
  382. if ((rc = doUnlink(tname, delP, 0)) != 0)
  383. return rc;
  384. /* copy the file */
  385. if (SP->dontdoit)
  386. rc = 0;
  387. else
  388. fsCopy(sname, tname, 0, &rc);
  389. /* check for errors */
  390. ERROR_CHECK(op_copy, sname, rc);
  391. return rc;
  392. }
  393. /*
  394. * doMkdir: create a new sub directory in the target directory
  395. */
  396. static int
  397. doMkdir(char *sname, DirEntry *sP, char *tname, DirEntry *delP)
  398. {
  399. int rc;
  400. /* display message */
  401. CONFIRM(op_mkdir, sname, sP->ftype, tname, DTYPE(delP), "", rc);
  402. /* if necessary, delete target */
  403. if (delP && !SP->dontdoit)
  404. if ((rc = doUnlink(tname, delP, 0)) != 0)
  405. return rc;
  406. /* make a new directory */
  407. rc = SP->dontdoit? 0: (mkdir(tname, sP->stat.st_mode & 07777) < 0)? errno: 0;
  408. /* check for errors */
  409. ERROR_CHECK(op_mkdir, tname, rc);
  410. return rc;
  411. }
  412. /*
  413. * targetLink: lookup symbloc link in the maplink list
  414. */
  415. static void
  416. targetLink(char *source, char *target, char **tlPP, char **msgPP)
  417. /*
  418. *
  419. */
  420. {
  421. static char buf[1024] = " -> ";
  422. MapList *ml;
  423. int l;
  424. for (ml = SP->maplink; ml; ml = ml->next) {
  425. l = strlen(ml->from);
  426. if (strncmp(ml->from, source, l) == 0
  427. && (source[l] == '/' || source[l] == 0))
  428. {
  429. strcpy(buf + 4, ml->to);
  430. if (source[l])
  431. strcat(buf, source +l);
  432. if (strcmp(buf + 4, target) != 0) {
  433. *tlPP = buf + 4;
  434. *msgPP = buf;
  435. return;
  436. }
  437. }
  438. }
  439. *tlPP = source;
  440. *msgPP = "";
  441. }
  442. /*
  443. * doCopyLink: copy a symbolic link from the source to target directory
  444. */
  445. static int
  446. doCopyLink(char *sname, DirEntry *sP, char *tname, DirEntry *delP)
  447. {
  448. char *tl, *msg;
  449. int rc;
  450. /* determine where the link should point */
  451. targetLink(sP->link, tname, &tl, &msg);
  452. /* display message */
  453. CONFIRM(op_cplink, sname, sP->ftype, tname, DTYPE(delP), msg, rc);
  454. /* if necessary, delete target */
  455. if (delP && !SP->dontdoit)
  456. if ((rc = doUnlink(tname, delP, 0)) != 0)
  457. return rc;
  458. /* create link */
  459. rc = SP->dontdoit? 0: (symlink(tl, tname) != 0)? errno: 0;
  460. /* check for errors */
  461. ERROR_CHECK(op_cplink, tname, rc);
  462. return rc;
  463. }
  464. /*
  465. * doSymlink: create a symbolic link in the target directory to a file
  466. * in the source directory
  467. */
  468. static int
  469. doSymlink(char *sname, DirEntry *sP, char *tname, DirEntry *delP)
  470. {
  471. char *tl, *msg;
  472. int rc;
  473. /* determine where the link should point */
  474. targetLink(sname, tname, &tl, &msg);
  475. /* display message */
  476. CONFIRM(op_mklink, sname, sP->ftype, tname, DTYPE(delP), msg, rc);
  477. /* if necessary, delete target */
  478. if (delP && !SP->dontdoit)
  479. if ((rc = doUnlink(tname, delP, 0)) != 0)
  480. return rc;
  481. /* create link */
  482. targetLink(sname, tname, &tl, &msg);
  483. rc = SP->dontdoit? 0: (symlink(tl, tname) != 0)? errno: 0;
  484. /* check for errors */
  485. ERROR_CHECK(op_mklink, sname, rc);
  486. return rc;
  487. }
  488. /*
  489. * cmpSymlink: compare two symbolic links
  490. * return values:
  491. * -1 error reading one of the links
  492. * 0 links point to the same file
  493. * 1 tname link points to sname
  494. * 2 links point to different files
  495. */
  496. static int
  497. cmpSymlink(char *sname, DirEntry *seP, char *tname, DirEntry *teP)
  498. {
  499. char *tl, *msg;
  500. /* return error if target link could not be read */
  501. if (!teP->link)
  502. return -1;
  503. /* check if target link points to source link */
  504. if (SP->keeplinks) {
  505. targetLink(sname, tname, &tl, &msg);
  506. if (strcmp(teP->link, tl) == 0)
  507. return 1;
  508. }
  509. /* if source not a symbolic link, target must be pointing to wrong file */
  510. if (!(seP->ftype & ft_islnk))
  511. return 2;
  512. /* compare the links */
  513. targetLink(seP->link, tname, &tl, &msg);
  514. if (strcmp(teP->link, tl) == 0)
  515. return 0;
  516. else
  517. return 2;
  518. }
  519. /*--------------------------------------------------------------------
  520. * sync directories
  521. *------------------------------------------------------------------*/
  522. static int SyncDirRecur(char *sdir, char *tdir);
  523. /*
  524. * SyncItem: copy or replace one item from the source to the target directory
  525. */
  526. static int
  527. SyncItem(char *sname, DirEntry *sP, char *tname, DirEntry *tP)
  528. {
  529. int rc;
  530. if (periodicCallback && (*periodicCallback)() != 0)
  531. return -1;
  532. if (!tP) {
  533. /* target does not exist */
  534. rpl_target:
  535. /*--------------------------------------------------------------------
  536. * target does not exist or should be replaced
  537. *------------------------------------------------------------------*/
  538. /* check if -dontreplace or -dontadd options apply */
  539. if (tP && SP->dontreplace || !tP && SP->dontadd)
  540. return 0;
  541. if (sP->ftype & ft_islnk) {
  542. /* source is a link */
  543. if ((sP->ftype & ft_isdir) && !SP->copydirs ||
  544. !(sP->ftype & ft_isdir) && !SP->copyfiles)
  545. {
  546. /* just copy the link */
  547. return doCopyLink(sname, sP, tname, tP);
  548. }
  549. } else {
  550. /* source is not a link */
  551. if ((sP->ftype & ft_isdir) && SP->linkdirs ||
  552. !(sP->ftype & ft_isdir) && SP->linkfiles)
  553. {
  554. /* don't copy; just create a link to the target */
  555. return doSymlink(sname, sP, tname, tP);
  556. }
  557. }
  558. if (sP->ftype & ft_isdir) {
  559. /* source is a dir: create target directory and do recursive sync */
  560. rc = doMkdir(sname, sP, tname, tP);
  561. if (rc || SP->dontdoit)
  562. return rc;
  563. else
  564. return SyncDirRecur(sname, tname);
  565. } else
  566. /* source is a regular file: copy the file */
  567. return doCopy(sname, sP, tname, tP);
  568. } else if (tP->ftype & ft_islnk) {
  569. /*--------------------------------------------------------------------
  570. * target is a symbolic link
  571. *------------------------------------------------------------------*/
  572. if ((tP->ftype & ft_isdir) == (sP->ftype & ft_isdir)) {
  573. /*
  574. * target points to the right type of object (dir vs regular file):
  575. * check if target points to the right place
  576. */
  577. rc = cmpSymlink(sname, sP, tname, tP);
  578. if (rc < 0)
  579. return 1; /* something is wrong */
  580. if (SP->listlinks && rc == 1)
  581. CONFIRM(op_info_link, sname, sP->ftype, tname, tP->ftype, "", rc);
  582. if (rc < 2)
  583. /* target is pointing to the right place */
  584. return 0;
  585. }
  586. /*
  587. * target points to the wrong type of object:
  588. * delete target link and try again
  589. */
  590. goto rpl_target;
  591. } else if (!(tP->ftype & ft_isdir)) {
  592. /*--------------------------------------------------------------------
  593. * target is regular file
  594. *------------------------------------------------------------------*/
  595. if (sP->ftype & ft_isdir)
  596. /* source is a dir: delete target file and try again */
  597. goto rpl_target;
  598. else {
  599. /* source is regular file or link to regular file */
  600. if (sP->ftype & ft_islnk) {
  601. /* source is just a link */
  602. if (!SP->keepcopies && !SP->copyfiles)
  603. /* replace target by a link */
  604. goto rpl_target;
  605. else if (SP->listcopies)
  606. CONFIRM(op_info_copy, sname, sP->ftype, tname, tP->ftype, "", rc);
  607. }
  608. if (!SP->forcecopies &&
  609. tP->stat.st_mtime == sP->stat.st_mtime &&
  610. tP->stat.st_size == sP->stat.st_size)
  611. {
  612. /* we assume the file hasn't changed: don't do anything */
  613. return 0;
  614. }
  615. if (SP->keepnew && tP->stat.st_mtime > sP->stat.st_mtime)
  616. /* we should keep the target because it is newer than the source */
  617. return 0;
  618. /* replace the target file */
  619. goto rpl_target;
  620. }
  621. } else if (!(tP->ftype & ft_islnk)) {
  622. /*--------------------------------------------------------------------
  623. * target is a directory
  624. *------------------------------------------------------------------*/
  625. if (sP->ftype & ft_isdir) {
  626. if (sP->ftype & ft_islnk) {
  627. /* source is just a link */
  628. if (!SP->keepcopies && !SP->copydirs)
  629. /* replace target by a link */
  630. goto rpl_target;
  631. else if (SP->listcopies)
  632. CONFIRM(op_info_copy, sname, sP->ftype, tname, tP->ftype, "", rc);
  633. }
  634. /* source is dir: recursively sync source and target dirs */
  635. return SyncDirRecur(sname, tname);
  636. } else
  637. /* source is no dir: delete target dir and try again */
  638. goto rpl_target;
  639. }
  640. /* should never be reached */
  641. return -1;
  642. }
  643. /*
  644. * CmpPred: returns a precedence number that is used to determine in which
  645. * order source directory entries are processed:
  646. * 1 source is regular file
  647. * 2 source is symbolic link
  648. * 3 target is not a real directory
  649. * 4 target exists and is a real directory (not a sym link)
  650. */
  651. static int
  652. CmpPred(DirEntry *dP)
  653. {
  654. if (!(dP->ftype & ft_isdir))
  655. return 1;
  656. else if (dP->ftype & ft_islnk)
  657. return 2;
  658. else if (!dP->teP ||
  659. !(dP->teP->ftype & ft_isdir) ||
  660. (dP->teP->ftype & ft_islnk))
  661. {
  662. return 3;
  663. } else
  664. return 4;
  665. }
  666. /*
  667. * DirCmp2: compare function for sorting directory entries
  668. */
  669. static int
  670. DirCmp2(const void *p1, const void *p2)
  671. {
  672. int d = CmpPred(*(DirEntry **)p1) - CmpPred(*(DirEntry **)p2);
  673. if (d)
  674. return d;
  675. else
  676. return strcmp((*(DirEntry **)p1)->name, (*(DirEntry **)p2)->name);
  677. }
  678. /*
  679. * SyncDirRecur: recursively synchronize source and target directories
  680. */
  681. static int
  682. SyncDirRecur(char *sdir, char *tdir)
  683. {
  684. DirEntry *sP, *tP, *seP, *teP;
  685. char *fsP, *ftP;
  686. int rc;
  687. /* informational callback */
  688. if (SP->verbose)
  689. CONFIRM(op_sync, sdir, ft_isdir, tdir, ft_isdir, "", rc);
  690. /* read source and target directories */
  691. if ((rc = GetDir(sdir, SP->exclude, SP->skip, &sP)) != 0)
  692. return rc;
  693. if ((rc = GetDir(tdir, NULL, SP->skip, &tP)) != 0) {
  694. FreeDir(sP);
  695. return rc;
  696. }
  697. /* append "/" to source and target directory names */
  698. fsP = sdir + strlen(sdir);
  699. ftP = tdir + strlen(tdir);
  700. *fsP++ = '/';
  701. *ftP++ = '/';
  702. /* first delete files in target dir that don't exist in source dir */
  703. seP = sP;
  704. teP = tP;
  705. while (teP) {
  706. /* compare directory entries */
  707. rc = (seP != NULL)? strcmp(seP->name, teP->name): 1;
  708. if ((seP != NULL) && seP->exclude) {
  709. /* skip excluded files */
  710. seP = seP->next;
  711. } else if (rc == 0) {
  712. /* file exists in both directories */
  713. seP->teP = teP;
  714. seP = seP->next;
  715. teP = teP->next;
  716. } else if (rc < 0) {
  717. /* source file not in target directory */
  718. seP = seP->next;
  719. } else { /* rc > 0 */
  720. /* target file not in source directory */
  721. if (!SP->dontdelete && !teP->skip) {
  722. strcpy(ftP, teP->name);
  723. if (doUnlink(tdir, teP, 1) < 0) {
  724. FreeDir(sP);
  725. FreeDir(tP);
  726. return -1;
  727. }
  728. }
  729. teP = teP->next;
  730. }
  731. }
  732. /* re-sort source dir so that regular files are processed before sub dirs */
  733. sP = SortDir(sP, DirCmp2);
  734. /* process all entires found in the source directory */
  735. for (seP = sP, rc = 0; seP && rc >= 0; seP = seP->next) {
  736. /* skip error entries */
  737. if (seP->rc)
  738. continue;
  739. /* skip excluded and to-be-skipped files */
  740. if (seP->exclude || seP->skip)
  741. continue;
  742. /* also skip sub directories if "don't recur" option is set */
  743. if (SP->dontrecur && (seP->ftype & ft_isdir))
  744. continue;
  745. /* construct source and target file names */
  746. strcpy(fsP, seP->name);
  747. strcpy(ftP, seP->name);
  748. /* sync directory item */
  749. rc = SyncItem(sdir, seP, tdir, seP->teP);
  750. }
  751. /* free directory lists */
  752. FreeDir(sP);
  753. FreeDir(tP);
  754. return (rc < 0)? -1: 0;
  755. }
  756. /*--------------------------------------------------------------------
  757. * external entry point
  758. *------------------------------------------------------------------*/
  759. void
  760. SyncDirectory(SyncParams *p)
  761. {
  762. char sbuf[1024], tbuf[1024];
  763. DirEntry *sP, *tP;
  764. FileOp op;
  765. int rc;
  766. /* the lower-level error and progress callbacks are not used */
  767. errorCallback = NULL;
  768. progressCallback = NULL;
  769. /* save pointer to params; copy source & target names */
  770. SP = p;
  771. snprintf(sbuf, sizeof(sbuf), "%s", SP->source);
  772. snprintf(tbuf, sizeof(tbuf), "%s", SP->target);
  773. /* get info about the source */
  774. sP = GetDirEntry(sbuf, &op, &rc);
  775. if (rc) {
  776. if (syncErrorCallback)
  777. syncErrorCallback(op, sbuf, rc);
  778. FreeDir(sP);
  779. return;
  780. }
  781. /*
  782. * The "copytop" option means: if the top-level source is a symbolic
  783. * make a copy of the real thing. To get this behavior we just switch
  784. * off the ft_islnk flag on the source.
  785. */
  786. if (SP->copytop)
  787. sP->ftype &= ~ft_islnk;
  788. /* get info about the target */
  789. tP = GetDirEntry(tbuf, &op, &rc);
  790. if (rc) {
  791. if (op == op_lstat && rc == ENOENT) {
  792. /* target does not yet exist; that's ok */
  793. FreeDir(tP);
  794. tP = NULL;
  795. } else {
  796. /* we'll try to procede anyway ... unless the error callback says no */
  797. if (syncErrorCallback && syncErrorCallback(op, tbuf, rc) < 0) {
  798. FreeDir(sP);
  799. FreeDir(tP);
  800. return;
  801. }
  802. }
  803. }
  804. /* now do the real work */
  805. SyncItem(sbuf, sP, tbuf, tP);
  806. /* free storage and return */
  807. FreeDir(sP);
  808. FreeDir(tP);
  809. }