Advertisement
class101

Rsync 3.0.9 ACL backup/restore patch - http://goo.gl/WWJSv

Aug 13th, 2011
394
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Diff 98.21 KB | None | 0 0
  1. Index: backup.c
  2. ===================================================================
  3. --- backup.c    (revision 2)
  4. +++ backup.c    (working copy)
  5. @@ -143,7 +143,7 @@
  6.  #ifdef SUPPORT_XATTRS
  7.                 sx.xattr = NULL;
  8.  #endif
  9. -               if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
  10. +               if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS, rel)))
  11.                     continue;
  12.  #ifdef SUPPORT_ACLS
  13.                 if (preserve_acls && !S_ISLNK(file->mode)) {
  14. @@ -224,7 +224,7 @@
  15.     sx.xattr = NULL;
  16.  #endif
  17.  
  18. -   if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
  19. +   if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS, fname)))
  20.         return 1; /* the file could have disappeared */
  21.  
  22.     if (!(buf = get_backup_name(fname))) {
  23. Index: cleanup.c
  24. ===================================================================
  25. --- cleanup.c   (revision 2)
  26. +++ cleanup.c   (working copy)
  27. @@ -196,6 +196,14 @@
  28.  
  29.         /* FALLTHROUGH */
  30.  #include "case_N.h"
  31. +       fscache_save();
  32. +
  33. +       /* FALLTHROUGH */
  34. +#include "case_N.h"
  35. +       obf_save();
  36. +
  37. +       /* FALLTHROUGH */
  38. +#include "case_N.h"
  39.         switch_step++;
  40.  
  41.         if (verbose > 2) {
  42. Index: exclude.c
  43. ===================================================================
  44. --- exclude.c   (revision 2)
  45. +++ exclude.c   (working copy)
  46. @@ -36,6 +36,7 @@
  47.  extern int sanitize_paths;
  48.  extern int protocol_version;
  49.  extern int module_id;
  50. +extern char *obfuscation_file;
  51.  
  52.  extern char curr_dir[MAXPATHLEN];
  53.  extern unsigned int curr_dir_len;
  54. @@ -1167,6 +1168,7 @@
  55.  static void send_rules(int f_out, struct filter_list_struct *flp)
  56.  {
  57.     struct filter_struct *ent, *prev = NULL;
  58. +   char obf_pattern_buf[MAXPATHLEN], *pattern;
  59.  
  60.     for (ent = flp->head; ent; ent = ent->next) {
  61.         unsigned int len, plen, dlen;
  62. @@ -1204,7 +1206,17 @@
  63.             if (f == f_out)
  64.                 continue;
  65.         }
  66. -       p = get_rule_prefix(ent->match_flags, ent->pattern, 1, &plen);
  67. +
  68. +       if (obfuscation_file) {
  69. +           obfuscate_pattern(obf_pattern_buf, MAXPATHLEN, ent->pattern);
  70. +           pattern = obf_pattern_buf;
  71. +           if (verbose > 2)
  72. +               rprintf(FINFO, "obfuscating pattern \"%s\" -> \"%s\"\n", ent->pattern, obf_pattern_buf);
  73. +       }
  74. +       else
  75. +           pattern = ent->pattern;
  76. +
  77. +       p = get_rule_prefix(ent->match_flags, pattern, 1, &plen);
  78.         if (!p) {
  79.             rprintf(FERROR,
  80.                 "filter rules are too modern for remote rsync.\n");
  81. @@ -1212,14 +1224,14 @@
  82.         }
  83.         if (f_out < 0)
  84.             continue;
  85. -       len = strlen(ent->pattern);
  86. +       len = strlen(pattern);
  87.         dlen = ent->match_flags & MATCHFLG_DIRECTORY ? 1 : 0;
  88.         if (!(plen + len + dlen))
  89.             continue;
  90.         write_int(f_out, plen + len + dlen);
  91.         if (plen)
  92.             write_buf(f_out, p, plen);
  93. -       write_buf(f_out, ent->pattern, len);
  94. +       write_buf(f_out, pattern, len);
  95.         if (dlen)
  96.             write_byte(f_out, '/');
  97.     }
  98. Index: flist.c
  99. ===================================================================
  100. --- flist.c (revision 2)
  101. +++ flist.c (working copy)
  102. @@ -70,6 +70,7 @@
  103.  extern uid_t our_uid;
  104.  extern struct stats stats;
  105.  extern char *filesfrom_host;
  106. +extern char *obfuscation_file;
  107.  
  108.  extern char curr_dir[MAXPATHLEN];
  109.  
  110. @@ -78,6 +79,8 @@
  111.  extern struct filter_list_struct filter_list;
  112.  extern struct filter_list_struct daemon_filter_list;
  113.  
  114. +extern int backup_nt_streams;
  115. +
  116.  #ifdef ICONV_OPTION
  117.  extern int filesfrom_convert;
  118.  extern iconv_t ic_send, ic_recv;
  119. @@ -288,7 +291,7 @@
  120.  }
  121.  
  122.  static void send_directory(int f, struct file_list *flist,
  123. -              char *fbuf, int len, int flags);
  124. +              char *fbuf, int len, int flags, const char *parentdir);
  125.  
  126.  static const char *pathname, *orig_dir;
  127.  static int pathname_len;
  128. @@ -630,6 +633,18 @@
  129.         stats.total_size += F_LENGTH(file);
  130.  }
  131.  
  132. +static void add_obf(struct file_struct *file)
  133. +{
  134. +   if (obfuscation_file) {
  135. +       file->dirname_obf = file->dirname ? obf_lookup_dirname(file->dirname) : NULL;
  136. +       file->basename_obf = obf_lookup_basename(file->basename);
  137. +   } else {
  138. +       file->dirname_obf = file->dirname;
  139. +       file->basename_obf = file->basename;
  140. +   }
  141. +
  142. +}
  143. +
  144.  static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
  145.  {
  146.     static int64 modtime;
  147. @@ -697,6 +712,9 @@
  148.     }
  149.  #endif
  150.  
  151. +   if (obfuscation_file)
  152. +       deobfuscate_fname(thisname, MAXPATHLEN, thisname);
  153. +
  154.     if (*thisname)
  155.         clean_fname(thisname, 0);
  156.  
  157. @@ -1060,6 +1078,8 @@
  158.     if (S_ISREG(mode) || S_ISLNK(mode))
  159.         stats.total_size += file_length;
  160.  
  161. +   add_obf(file);
  162. +
  163.     return file;
  164.  }
  165.  
  166. @@ -1074,12 +1094,13 @@
  167.   * "io_error |= IOERR_GENERAL" to avoid deletion of the file from the
  168.   * destination if --delete is on. */
  169.  struct file_struct *make_file(const char *fname, struct file_list *flist,
  170. -                 STRUCT_STAT *stp, int flags, int filter_level)
  171. +                 STRUCT_STAT *stp, int flags, int filter_level, const char *real_fname)
  172.  {
  173.     static char *lastdir;
  174.     static int lastdir_len = -1;
  175.     struct file_struct *file;
  176.     char thisname[MAXPATHLEN];
  177. +   char real_thisname[MAXPATHLEN];
  178.     char linkname[MAXPATHLEN];
  179.     int alloc_len, basename_len, linkname_len;
  180.     int extra_len = file_extra_cnt * EXTRA_LEN;
  181. @@ -1097,15 +1118,24 @@
  182.     if (sanitize_paths)
  183.         sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
  184.  
  185. +   if (strlcpy(real_thisname, real_fname, sizeof real_thisname) >= sizeof real_thisname) {
  186. +       io_error |= IOERR_GENERAL;
  187. +       rprintf(FERROR_XFER, "skipping overly long name: %s\n", real_fname);
  188. +       return NULL;
  189. +   }
  190. +   clean_fname(real_thisname, 0);
  191. +   if (sanitize_paths)
  192. +       sanitize_path(real_thisname, real_thisname, "", 0, SP_DEFAULT);
  193. +
  194.     if (stp && S_ISDIR(stp->st_mode)) {
  195.         st = *stp; /* Needed for "symlink/." with --relative. */
  196.         *linkname = '\0'; /* make IBM code checker happy */
  197. -   } else if (readlink_stat(thisname, &st, linkname) != 0) {
  198. +   } else if (readlink_stat(real_thisname, &st, linkname) != 0) {
  199.         int save_errno = errno;
  200.         /* See if file is excluded before reporting an error. */
  201.         if (filter_level != NO_FILTERS
  202. -        && (is_excluded(thisname, 0, filter_level)
  203. -         || is_excluded(thisname, 1, filter_level))) {
  204. +        && (is_excluded(real_thisname, 0, filter_level)
  205. +         || is_excluded(real_thisname, 1, filter_level))) {
  206.             if (ignore_perishable && save_errno != ENOENT)
  207.                 non_perishable_cnt++;
  208.             return NULL;
  209. @@ -1119,34 +1149,42 @@
  210.              * options was specified, so there's no need for the
  211.              * extra lstat() if one of these options isn't on. */
  212.             if ((copy_links || copy_unsafe_links || copy_dirlinks)
  213. -            && x_lstat(thisname, &st, NULL) == 0
  214. +            && x_lstat(real_thisname, &st, NULL) == 0
  215.              && S_ISLNK(st.st_mode)) {
  216.                 io_error |= IOERR_GENERAL;
  217.                 rprintf(FERROR_XFER, "symlink has no referent: %s\n",
  218. -                   full_fname(thisname));
  219. +                   full_fname(real_thisname));
  220.             } else
  221.  #endif
  222.             {
  223.                 enum logcode c = am_daemon && protocol_version < 28
  224.                            ? FERROR : FWARNING;
  225.                 io_error |= IOERR_VANISHED;
  226. -               rprintf(c, "file has vanished: %s\n",
  227. -                   full_fname(thisname));
  228. +               rprintf(c, "make_file: file has vanished: %s\n",
  229. +                   full_fname(real_thisname));
  230.             }
  231.         } else {
  232.             io_error |= IOERR_GENERAL;
  233.             rsyserr(FERROR_XFER, save_errno, "readlink_stat(%s) failed",
  234. -               full_fname(thisname));
  235. +               full_fname(real_thisname));
  236.         }
  237.         return NULL;
  238.     }
  239.  
  240. +   /* NT streams files should not be directories, even if the base file is. */
  241. +   if (flags & FLAG_NT_STREAM) {
  242. +       st.st_mode &= ~S_IFDIR;
  243. +       st.st_mode |= S_IFREG;
  244. +   }
  245. +
  246. +   fscache_lookup(thisname, &st.st_size);
  247. +
  248.     if (filter_level == NO_FILTERS)
  249.         goto skip_filters;
  250.  
  251.     if (S_ISDIR(st.st_mode)) {
  252.         if (!xfer_dirs) {
  253. -           rprintf(FINFO, "skipping directory %s\n", thisname);
  254. +           rprintf(FINFO, "skipping directory %s\n", real_thisname);
  255.             return NULL;
  256.         }
  257.         /* -x only affects dirs because we need to avoid recursing
  258. @@ -1158,7 +1196,7 @@
  259.                 if (verbose > 1) {
  260.                     rprintf(FINFO,
  261.                         "[%s] skipping mount-point dir %s\n",
  262. -                       who_am_i(), thisname);
  263. +                       who_am_i(), real_thisname);
  264.                 }
  265.                 return NULL;
  266.             }
  267. @@ -1168,7 +1206,7 @@
  268.     } else
  269.         flags &= ~FLAG_CONTENT_DIR;
  270.  
  271. -   if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
  272. +   if (is_excluded(real_thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
  273.         if (ignore_perishable)
  274.             non_perishable_cnt++;
  275.         return NULL;
  276. @@ -1178,7 +1216,7 @@
  277.  #ifdef SUPPORT_LINKS
  278.         if (!S_ISLNK(st.st_mode))
  279.  #endif
  280. -           if (access(thisname, R_OK) != 0)
  281. +           if (access(real_thisname, R_OK) != 0)
  282.                 return NULL;
  283.     }
  284.  
  285. @@ -1300,6 +1338,10 @@
  286.  
  287.     if (always_checksum && am_sender && S_ISREG(st.st_mode))
  288.         file_checksum(thisname, tmp_sum, st.st_size);
  289. +           // XXX if we use thisname here then --checksum option will
  290. +           // result in nt streams files always being sent - dmo.
  291. +           // However, at this stage the nt streams file doesn't exist so
  292. +           // I'm not sure what else to do.
  293.  
  294.     if (am_sender)
  295.         F_PATHNAME(file) = pathname;
  296. @@ -1315,6 +1357,8 @@
  297.     if (unsort_ndx)
  298.         F_NDX(file) = dir_count;
  299.  
  300. +   add_obf(file);
  301. +
  302.     return file;
  303.  }
  304.  
  305. @@ -1326,11 +1370,11 @@
  306.  
  307.  static struct file_struct *send_file_name(int f, struct file_list *flist,
  308.                       const char *fname, STRUCT_STAT *stp,
  309. -                     int flags, int filter_level)
  310. +                     int flags, int filter_level, const char *real_fname)
  311.  {
  312.     struct file_struct *file;
  313.  
  314. -   file = make_file(fname, flist, stp, flags, filter_level);
  315. +   file = make_file(fname, flist, stp, flags, filter_level, real_fname);
  316.     if (!file)
  317.         return NULL;
  318.  
  319. @@ -1414,13 +1458,13 @@
  320.  #endif
  321.         } else
  322.  #endif
  323. -           f_name(file, fbuf);
  324. +           f_name_maybe_obf(file, fbuf);
  325.  
  326.  #ifdef SUPPORT_ACLS
  327.         if (preserve_acls && !S_ISLNK(file->mode)) {
  328.             sx.st.st_mode = file->mode;
  329.             sx.acc_acl = sx.def_acl = NULL;
  330. -           if (get_acl(fname, &sx) < 0) {
  331. +           if (get_acl(real_fname, &sx) < 0) {
  332.                 io_error |= IOERR_GENERAL;
  333.                 return NULL;
  334.             }
  335. @@ -1430,7 +1474,7 @@
  336.         if (preserve_xattrs) {
  337.             sx.st.st_mode = file->mode;
  338.             sx.xattr = NULL;
  339. -           if (get_xattr(fname, &sx) < 0) {
  340. +           if (get_xattr(real_fname, &sx) < 0) {
  341.                 io_error |= IOERR_GENERAL;
  342.                 return NULL;
  343.             }
  344. @@ -1479,7 +1523,8 @@
  345.         if (len > 1 && fbuf[len-1] == '/')
  346.             fbuf[--len] = '\0';
  347.         save_filters = push_local_filters(fbuf, len);
  348. -       send_directory(f, flist, fbuf, len, flags);
  349. +       flags |= file->flags & FLAG_NT_STREAM_DIR;
  350. +       send_directory(f, flist, fbuf, len, flags, file->dirname);
  351.         pop_local_filters(save_filters);
  352.         fbuf[ol] = '\0';
  353.         if (is_dot_dir)
  354. @@ -1593,11 +1638,12 @@
  355.         DIR_NEXT_SIBLING(dp) = -1;
  356.  }
  357.  
  358. -static void interpret_stat_error(const char *fname, int is_dir)
  359. +static void interpret_stat_error(const char *func_name, const char *fname, int is_dir)
  360.  {
  361.     if (errno == ENOENT) {
  362.         io_error |= IOERR_VANISHED;
  363. -       rprintf(FWARNING, "%s has vanished: %s\n",
  364. +       rprintf(FWARNING, "%s: %s has vanished: %s\n",
  365. +           func_name,
  366.             is_dir ? "directory" : "file", full_fname(fname));
  367.     } else {
  368.         io_error |= IOERR_GENERAL;
  369. @@ -1612,7 +1658,7 @@
  370.   * might call this with f set to -2, which also indicates that local filter
  371.   * rules should be ignored. */
  372.  static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
  373. -              int flags)
  374. +              int flags, const char *parentdir)
  375.  {
  376.     struct dirent *di;
  377.     unsigned remainder;
  378. @@ -1622,13 +1668,21 @@
  379.     int start = flist->used;
  380.     int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS;
  381.  
  382. +   char dirname[MAXPATHLEN];
  383. +   strncpy(dirname, fbuf, len);
  384. +   dirname[len] = '\0';
  385. +
  386.     assert(flist != NULL);
  387.  
  388. -   if (!(d = opendir(fbuf))) {
  389. +   if (!(d = opendir(flags & FLAG_NT_STREAM_DIR ? (parentdir ? parentdir : ".") : fbuf))) {
  390.         if (errno == ENOENT) {
  391.             if (am_sender) /* Can abuse this for vanished error w/ENOENT: */
  392. -               interpret_stat_error(fbuf, True);
  393. +               interpret_stat_error("send_directory", fbuf, True);
  394.             return;
  395. +       } else if (errno == ENOTDIR) {
  396. +           if (am_sender)
  397. +               rprintf(FWARNING, "opendir: not a directory: %s\n", fbuf);
  398. +           return;
  399.         }
  400.         io_error |= IOERR_GENERAL;
  401.         rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf));
  402. @@ -1645,7 +1699,10 @@
  403.     } else
  404.         remainder = 0;
  405.  
  406. +   if (flags & FLAG_NT_STREAM_DIR)
  407. +       flags |= FLAG_NT_STREAM;
  408.     for (errno = 0, di = readdir(d); di; errno = 0, di = readdir(d)) {
  409. +       char orig_ntbuf[MAXPATHLEN];
  410.         char *dname = d_name(di);
  411.         if (dname[0] == '.' && (dname[1] == '\0'
  412.             || (dname[1] == '.' && dname[2] == '\0')))
  413. @@ -1669,9 +1726,16 @@
  414.             continue;
  415.         }
  416.  
  417. -       send_file_name(f, flist, fbuf, NULL, flags, filter_level);
  418. +       ntstreams_orig_filename(orig_ntbuf, MAXPATHLEN, dirname, dname);
  419. +       send_file_name(f, flist, fbuf, NULL, flags, filter_level, orig_ntbuf);
  420.     }
  421. +   if (backup_nt_streams && !(flags & FLAG_NT_STREAM_DIR)) {
  422. +       char ntbuf[MAXPATHLEN];
  423. +       ntstreams_dirname(ntbuf, MAXPATHLEN, dirname);
  424. +       send_file_name(f, flist, ntbuf, NULL, flags | FLAG_NT_STREAM_DIR, filter_level, dirname);
  425. +   }
  426.  
  427. +
  428.     fbuf[len] = '\0';
  429.  
  430.     if (errno) {
  431. @@ -1681,11 +1745,13 @@
  432.  
  433.     closedir(d);
  434.  
  435. -   if (f >= 0 && recurse && !divert_dirs) {
  436. +   if (f >= 0 && recurse) {
  437.         int i, end = flist->used - 1;
  438.         /* send_if_directory() bumps flist->used, so use "end". */
  439. -       for (i = start; i <= end; i++)
  440. -           send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
  441. +       for (i = start; i <= end; i++) {
  442. +           if (!divert_dirs)
  443. +               send_if_directory(f, flist, flist->files[i], fbuf, len, flags);
  444. +       }
  445.     }
  446.  }
  447.  
  448. @@ -1743,14 +1809,14 @@
  449.  
  450.         for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
  451.             *slash = '\0';
  452. -           file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
  453. +           file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS, fname);
  454.             depth++;
  455.             if (!inc_recurse && file && S_ISDIR(file->mode))
  456.                 change_local_filter_dir(fname, strlen(fname), depth);
  457.             *slash = '/';
  458.         }
  459.  
  460. -       file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
  461. +       file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS, fname);
  462.         if (inc_recurse) {
  463.             if (file && !S_ISDIR(file->mode))
  464.                 file = NULL;
  465. @@ -1818,12 +1884,12 @@
  466.         if (one_file_system) {
  467.             STRUCT_STAT st;
  468.             if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
  469. -               interpret_stat_error(fbuf, True);
  470. +               interpret_stat_error("send1extra (1)", fbuf, True);
  471.                 return;
  472.             }
  473.             filesystem_dev = st.st_dev;
  474.         }
  475. -       send_directory(f, flist, fbuf, dlen, flags);
  476. +       send_directory(f, flist, fbuf, dlen, flags | (file->flags & FLAG_NT_STREAM_DIR), file->dirname);
  477.     }
  478.  
  479.     if (!relative_paths)
  480. @@ -1853,12 +1919,12 @@
  481.         if (name_type != NORMAL_NAME) {
  482.             STRUCT_STAT st;
  483.             if (link_stat(fbuf, &st, 1) != 0) {
  484. -               interpret_stat_error(fbuf, True);
  485. +               interpret_stat_error("send1extra (2)", fbuf, True);
  486.                 continue;
  487.             }
  488. -           send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
  489. +           send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS, fbuf);
  490.         } else
  491. -           send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS);
  492. +           send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS, fbuf);
  493.     }
  494.  
  495.     free(relname_list);
  496. @@ -2104,7 +2170,7 @@
  497.             if (*fn == '.' && fn[1] == '/' && !implied_dot_dir) {
  498.                 send_file_name(f, flist, ".", NULL,
  499.                     (flags | FLAG_IMPLIED_DIR) & ~FLAG_CONTENT_DIR,
  500. -                   ALL_FILTERS);
  501. +                   ALL_FILTERS, ".");
  502.                 implied_dot_dir = 1;
  503.             }
  504.             len = clean_fname(fn, CFN_KEEP_TRAILING_SLASH
  505. @@ -2196,7 +2262,7 @@
  506.             struct file_struct *file;
  507.             file = send_file_name(f, flist, fbuf, &st,
  508.                           FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
  509. -                         NO_FILTERS);
  510. +                         NO_FILTERS, fbuf);
  511.             if (!file)
  512.                 continue;
  513.             if (inc_recurse) {
  514. @@ -2205,12 +2271,12 @@
  515.                         send_dir_depth = 0;
  516.                         change_local_filter_dir(fbuf, len, send_dir_depth);
  517.                     }
  518. -                   send_directory(f, flist, fbuf, len, flags);
  519. +                   send_directory(f, flist, fbuf, len, flags, file->dirname);
  520.                 }
  521.             } else
  522.                 send_if_directory(f, flist, file, fbuf, len, flags);
  523.         } else
  524. -           send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
  525. +           send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS, fbuf);
  526.     }
  527.  
  528.     gettimeofday(&end_tv, NULL);
  529. @@ -2880,6 +2946,7 @@
  530.  {
  531.     int dif;
  532.     const uchar *c1, *c2;
  533. +   char *f1_dirname, *f2_dirname, *f1_basename, *f2_basename;
  534.     enum fnc_state state1, state2;
  535.     enum fnc_type type1, type2;
  536.     enum fnc_type t_path = protocol_version >= 29 ? t_PATH : t_ITEM;
  537. @@ -2892,13 +2959,26 @@
  538.     if (!f2 || !F_IS_ACTIVE(f2))
  539.         return 1;
  540.  
  541. -   c1 = (uchar*)f1->dirname;
  542. -   c2 = (uchar*)f2->dirname;
  543. +   if (obfuscation_file) {
  544. +       f1_dirname = (uchar*)f1->dirname_obf;
  545. +       f2_dirname = (uchar*)f2->dirname_obf;
  546. +       f1_basename = (uchar*)f1->basename_obf;
  547. +       f2_basename = (uchar*)f2->basename_obf;
  548. +   } else {
  549. +       f1_dirname = (uchar*)f1->dirname;
  550. +       f2_dirname = (uchar*)f2->dirname;
  551. +       f1_basename = (uchar*)f1->basename;
  552. +       f2_basename = (uchar*)f2->basename;
  553. +   }
  554. +
  555. +
  556. +   c1 = (uchar*)f1_dirname;
  557. +   c2 = (uchar*)f2_dirname;
  558.     if (c1 == c2)
  559.         c1 = c2 = NULL;
  560.     if (!c1) {
  561.         type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
  562. -       c1 = (const uchar*)f1->basename;
  563. +       c1 = (const uchar*)f1_basename;
  564.         if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
  565.             type1 = t_ITEM;
  566.             state1 = s_TRAILING;
  567. @@ -2911,7 +2991,7 @@
  568.     }
  569.     if (!c2) {
  570.         type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
  571. -       c2 = (const uchar*)f2->basename;
  572. +       c2 = (const uchar*)f2_basename;
  573.         if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
  574.             type2 = t_ITEM;
  575.             state2 = s_TRAILING;
  576. @@ -2935,7 +3015,7 @@
  577.                 break;
  578.             case s_SLASH:
  579.                 type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM;
  580. -               c1 = (const uchar*)f1->basename;
  581. +               c1 = (const uchar*)f1_basename;
  582.                 if (type1 == t_PATH && *c1 == '.' && !c1[1]) {
  583.                     type1 = t_ITEM;
  584.                     state1 = s_TRAILING;
  585. @@ -2965,7 +3045,7 @@
  586.                 break;
  587.             case s_SLASH:
  588.                 type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM;
  589. -               c2 = (const uchar*)f2->basename;
  590. +               c2 = (const uchar*)f2_basename;
  591.                 if (type2 == t_PATH && *c2 == '.' && !c2[1]) {
  592.                     type2 = t_ITEM;
  593.                     state2 = s_TRAILING;
  594. @@ -3038,6 +3118,33 @@
  595.     return fbuf;
  596.  }
  597.  
  598. +char *f_name_obf(const struct file_struct *f, char *fbuf)
  599. +{
  600. +   if (!f || !F_IS_ACTIVE(f))
  601. +       return NULL;
  602. +
  603. +   if (!fbuf)
  604. +       fbuf = f_name_buf();
  605. +
  606. +   if (f->dirname_obf) {
  607. +       int len = strlen(f->dirname_obf);
  608. +       memcpy(fbuf, f->dirname_obf, len);
  609. +       fbuf[len] = '/';
  610. +       strlcpy(fbuf + len + 1, f->basename_obf, MAXPATHLEN - (len + 1));
  611. +   } else
  612. +       strlcpy(fbuf, f->basename_obf, MAXPATHLEN);
  613. +
  614. +   return fbuf;
  615. +}
  616. +
  617. +char *f_name_maybe_obf(const struct file_struct *f, char *fbuf)
  618. +{
  619. +   if (obfuscation_file)
  620. +       return f_name_obf(f, fbuf);
  621. +   else
  622. +       return f_name(f, fbuf);
  623. +}
  624. +
  625.  /* Do a non-recursive scan of the named directory, possibly ignoring all
  626.   * exclude rules except for the daemon's.  If "dlen" is >=0, it is the length
  627.   * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN
  628. @@ -3063,7 +3170,7 @@
  629.  
  630.     recurse = 0;
  631.     xfer_dirs = 1;
  632. -   send_directory(senddir_fd, dirlist, dirname, dlen, FLAG_CONTENT_DIR);
  633. +   send_directory(senddir_fd, dirlist, dirname, dlen, FLAG_CONTENT_DIR, NULL);
  634.     xfer_dirs = save_xfer_dirs;
  635.     recurse = save_recurse;
  636.     if (do_progress)
  637. Index: fscache.c
  638. ===================================================================
  639. --- fscache.c   (revision 0)
  640. +++ fscache.c   (revision 0)
  641. @@ -0,0 +1,257 @@
  642. +/*
  643. + * File size cache
  644. + *
  645. + * Copyright (C) 2010 Cortex IT
  646. + *
  647. + * This program is free software; you can redistribute it and/or modify
  648. + * it under the terms of the GNU General Public License as published by
  649. + * the Free Software Foundation; either version 3 of the License, or
  650. + * (at your option) any later version.
  651. + *
  652. + * This program is distributed in the hope that it will be useful,
  653. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  654. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  655. + * GNU General Public License for more details.
  656. + *
  657. + * You should have received a copy of the GNU General Public License along
  658. + * with this program; if not, visit the http://fsf.org website.
  659. + */
  660. +
  661. +#include "rsync.h"
  662. +
  663. +extern char *file_size_cache;
  664. +extern int verbose;
  665. +
  666. +struct fscache_node {
  667. +   char *fname;
  668. +   int64 size;
  669. +   int age;
  670. +   struct fscache_node *next;
  671. +};
  672. +
  673. +/* Increment this if you change the file format */
  674. +#define FSCACHE_VERSION 1
  675. +
  676. +/* how long to keep old obftable entries that have not been needed in this run */
  677. +#define FSCACHE_MAX_AGE 10
  678. +
  679. +#define FSCACHE_TABLE_SIZE 65537 /* prime number */
  680. +static struct fscache_node *fscache[FSCACHE_TABLE_SIZE];
  681. +
  682. +void fscache_clear(void)
  683. +{
  684. +   unsigned i;
  685. +   for (i = 0; i < FSCACHE_TABLE_SIZE; ++i)
  686. +   {
  687. +       struct fscache_node *p = fscache[i], *tmp;
  688. +       while (p)
  689. +       {
  690. +           tmp = p;
  691. +           p = p->next;
  692. +           free(tmp->fname);
  693. +           free(tmp);
  694. +       }
  695. +       fscache[i] = NULL;
  696. +   }
  697. +}
  698. +
  699. +static unsigned hash(const char *fname)
  700. +{
  701. +   unsigned n = 0;
  702. +   const char *p;
  703. +   for (p = fname; *p; ++p)
  704. +       n = (64 * n + *p) % FSCACHE_TABLE_SIZE;
  705. +   return n;
  706. +}
  707. +
  708. +static char *fix_fname(const char *fname)
  709. +{
  710. +   char *ret = normalize_path((char *)fname, 1, NULL);
  711. +#ifdef __CYGWIN__
  712. +   /* This is a nasty hack to remove the drive letter from the start of the path.
  713. +    * The reason we do this is because we want to be able to use this with VSS snapshots
  714. +    * and the drive letter mapped to the VSS snapshot is not guaranteed to be the same from
  715. +    * one run to the next. */
  716. +   if (strstr(ret, "/cygdrive/") == ret && strlen(ret) > 12) {
  717. +       char *old = ret;
  718. +       ret = strdup(ret + 12);
  719. +       free(old);
  720. +   }
  721. +#endif
  722. +   return ret;
  723. +}
  724. +
  725. +int fscache_lookup(const char *fname, int64 *pSize)
  726. +{
  727. +   if (!file_size_cache)
  728. +       return 0;
  729. +   struct fscache_node *p;
  730. +   char *norm_fname = fix_fname(fname);
  731. +
  732. +   if (verbose > 3)
  733. +       rprintf(FINFO, "fscache lookup %s\n", norm_fname);
  734. +
  735. +   for (p = fscache[hash(norm_fname)]; p; p = p->next)
  736. +   {
  737. +       int cmp = strcmp(p->fname, norm_fname);
  738. +       if (cmp == 0) {
  739. +           *pSize = p->size;
  740. +           p->age = 0; /* reset age */
  741. +           if (verbose > 2)
  742. +               rprintf(FINFO, "fscache found %s, size %ld\n", norm_fname, (long)*pSize);
  743. +           return 1;
  744. +       }
  745. +       if (cmp > 0)
  746. +           break;
  747. +   }
  748. +   if (verbose > 2)
  749. +       rprintf(FINFO, "fscache %s not found\n", norm_fname);
  750. +   return 0;
  751. +}
  752. +
  753. +static struct fscache_node *new_node(char *fname, int64 size, int age, struct fscache_node *next)
  754. +{
  755. +   struct fscache_node *p = new(struct fscache_node);
  756. +   p->fname = fname;
  757. +   p->size = size;
  758. +   p->age = age;
  759. +   p->next = next;
  760. +   return p;
  761. +}
  762. +
  763. +static void fscache_insert_internal(char *fname, int64 size, int age)
  764. +{
  765. +   struct fscache_node **p;
  766. +   if (verbose > 3)
  767. +       rprintf(FINFO, "fscache insert %s\n", fname);
  768. +
  769. +   for (p = fscache + hash(fname); *p; p = &(*p)->next)
  770. +   {
  771. +       int cmp = strcmp((*p)->fname, fname);
  772. +       if (cmp == 0) {
  773. +           // Update existing node.
  774. +           (*p)->size = size;
  775. +           (*p)->age = age;
  776. +           if (verbose > 2)
  777. +               rprintf(FINFO, "fscache updating %s, size %ld, age %d\n", fname, (long)size, age);
  778. +           return;
  779. +       }
  780. +       if (cmp > 0)
  781. +           break;
  782. +   }
  783. +   *p = new_node(fname, size, age, *p);
  784. +   if (verbose > 2)
  785. +       rprintf(FINFO, "fscache adding %s, size %ld, age %d, addr %p, next %p\n", fname, (long)size, age, (void *)(*p), (void *)(*p)->next);
  786. +}
  787. +
  788. +void fscache_insert(const char *fname, int64 size)
  789. +{
  790. +   if (!file_size_cache)
  791. +       return;
  792. +   char *norm_fname = fix_fname(fname);
  793. +   fscache_insert_internal(norm_fname, size, 0);
  794. +}
  795. +          
  796. +
  797. +int fscache_load()
  798. +{
  799. +   int fd;
  800. +   int32 version;
  801. +   char fname_buf[MAXPATHLEN];
  802. +   int64 size;
  803. +
  804. +   fscache_clear();
  805. +  
  806. +   fd = do_open(file_size_cache, O_RDONLY, 0);
  807. +   if (fd == -1) {
  808. +       if (verbose > 0)
  809. +           if (errno == ENOENT)
  810. +               rprintf(FINFO, "fscache file %s: does not exist, a new file will be created\n", file_size_cache);
  811. +           else
  812. +               rprintf(FINFO, "fscache file %s: could not open for loading\n", file_size_cache);
  813. +       return 0;
  814. +   }
  815. +   if (verbose > 0)
  816. +       rprintf(FINFO, "fscache file %s: loading\n", file_size_cache);
  817. +
  818. +   version = read_int(fd);
  819. +   if (version > FSCACHE_VERSION) {
  820. +       if (verbose > 0)
  821. +           rprintf(FINFO, "fscache file %s: version %d > latest known version (%d), ignoring\n", file_size_cache, version, FSCACHE_VERSION);
  822. +       return 0;
  823. +   }
  824. +
  825. +   while (1) {
  826. +       int age = 0;
  827. +       read_vstring(fd, fname_buf, MAXPATHLEN);
  828. +       if (strlen(fname_buf) == 0)
  829. +           break; // Empty string signifies that we've reached the end of the file.
  830. +
  831. +       size = read_longint(fd);
  832. +
  833. +       if (version >= 1)
  834. +           age = read_int(fd);
  835. +       age++;
  836. +       fscache_insert_internal(strdup(fname_buf), size, age);
  837. +   }
  838. +   close(fd);
  839. +   if (verbose > 0)
  840. +       rprintf(FINFO, "fscache file %s: loaded\n", file_size_cache);
  841. +   return 1;
  842. +}
  843. +
  844. +static void write_node(int fd, struct fscache_node *p)
  845. +{
  846. +   if (p->age > FSCACHE_MAX_AGE) {
  847. +       if (verbose > 2)
  848. +           rprintf(FINFO, "fscache discarding entry %s, size %ld, age %d\n", p->fname, (long)p->size, p->age);
  849. +       return;
  850. +   }
  851. +   if (verbose > 2)
  852. +       rprintf(FINFO, "fscache saving entry %s, size %ld, age %d\n", p->fname, (long)p->size, p->age);
  853. +   write_vstring(fd, p->fname, strlen(p->fname));
  854. +   write_longint(fd, p->size);
  855. +   write_int(fd, p->age);
  856. +}
  857. +
  858. +void fscache_save(void)
  859. +{
  860. +   int fd;
  861. +   unsigned i;
  862. +   char tmp[MAXPATHLEN];
  863. +
  864. +   if (!file_size_cache)
  865. +       return;
  866. +
  867. +   get_tmpname(tmp, file_size_cache);
  868. +
  869. +   fd = do_mkstemp(tmp, S_IRUSR|S_IWUSR);
  870. +   if (fd == -1) {
  871. +       if (verbose > 0)
  872. +           rprintf(FINFO, "fscache file %s: could not open for saving\n", tmp);
  873. +       return;
  874. +   }
  875. +   if (verbose > 2)
  876. +       rprintf(FINFO, "fscache file %s: saving\n", tmp);
  877. +
  878. +   write_int(fd, (int32)FSCACHE_VERSION);
  879. +      
  880. +   for (i = 0; i < FSCACHE_TABLE_SIZE; ++i) {
  881. +       struct fscache_node *p;
  882. +       for (p = fscache[i]; p; p = p->next)
  883. +           write_node(fd, p);
  884. +   }
  885. +
  886. +   // Write an empty string to mark the end of file.
  887. +   write_vstring(fd, "", 0);
  888. +
  889. +   close(fd);
  890. +
  891. +   if (verbose > 2)
  892. +       rprintf(FINFO, "fscache: renaming %s to %s\n", tmp, file_size_cache);
  893. +
  894. +   if (robust_rename(tmp, file_size_cache, NULL, S_IRUSR|S_IWUSR) < 0)
  895. +       rprintf(FERROR, "fscache: could not rename %s to %s\n", tmp, file_size_cache);
  896. +
  897. +   return;
  898. +}
  899. Index: generator.c
  900. ===================================================================
  901. --- generator.c (revision 2)
  902. +++ generator.c (working copy)
  903. @@ -60,6 +60,7 @@
  904.  extern int make_backups;
  905.  extern int csum_length;
  906.  extern int ignore_times;
  907. +extern int times_only;
  908.  extern int size_only;
  909.  extern OFF_T max_size;
  910.  extern OFF_T min_size;
  911. @@ -73,6 +74,7 @@
  912.  extern int fuzzy_basis;
  913.  extern int always_checksum;
  914.  extern int checksum_len;
  915. +extern char *basis_filter;
  916.  extern char *partial_dir;
  917.  extern char *basis_dir[MAX_BASIS_DIRS+1];
  918.  extern int compare_dest;
  919. @@ -96,6 +98,7 @@
  920.  extern int backup_suffix_len;
  921.  extern struct file_list *cur_flist, *first_flist, *dir_flist;
  922.  extern struct filter_list_struct daemon_filter_list;
  923. +extern int restore_nt_streams;
  924.  
  925.  int ignore_perishable = 0;
  926.  int non_perishable_cnt = 0;
  927. @@ -112,6 +115,7 @@
  928.  static int need_retouch_dir_times;
  929.  static int need_retouch_dir_perms;
  930.  static const char *solo_file = NULL;
  931. +static char *filter_argv[MAX_FILTER_ARGS + 1] = { NULL };
  932.  
  933.  /* For calling delete_item() and delete_dir_contents(). */
  934.  #define DEL_NO_UID_WRITE   (1<<0) /* file/dir has our uid w/o write perm */
  935. @@ -751,7 +755,7 @@
  936.  /* Perform our quick-check heuristic for determining if a file is unchanged. */
  937.  int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
  938.  {
  939. -   if (st->st_size != F_LENGTH(file))
  940. +   if (!times_only && st->st_size != F_LENGTH(file))
  941.         return 0;
  942.  
  943.     /* if always checksum is set then we use the checksum instead
  944. @@ -1315,6 +1319,7 @@
  945.     int is_dir = !S_ISDIR(file->mode) ? 0
  946.            : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
  947.            : 1;
  948. +   int is_ntstreams_file = 0;
  949.  
  950.     if (verbose > 2)
  951.         rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx);
  952. @@ -1405,7 +1410,27 @@
  953.             need_fuzzy_dirlist = 0;
  954.         }
  955.  
  956. -       statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
  957. +       if (restore_nt_streams)
  958. +       {
  959. +           /* If the file is an nt streams file then it won't
  960. +            * exist on the destination, however we don't want to
  961. +            * restore it unless we're restoring the base
  962. +            * (original) file.  To accomplish this we stat the
  963. +            * base file instead of the streams file so that
  964. +            * unchanged_file() will use that instead.  We then
  965. +            * need to adjust the mode to make sure it is a regular
  966. +            * file, not a directory.  For this to work,
  967. +            * --times-only option must also be in effect. */
  968. +           char ntstreams_fname[MAXPATHLEN];
  969. +           is_ntstreams_file = ntstreams_orig_filename(ntstreams_fname, MAXPATHLEN, file->dirname, file->basename);
  970. +           statret = link_stat(ntstreams_fname, &sx.st, keep_dirlinks && is_dir);
  971. +           if (is_ntstreams_file) {
  972. +               sx.st.st_mode &= ~S_IFDIR;
  973. +               sx.st.st_mode |= S_IFREG;
  974. +           }
  975. +       } else {
  976. +           statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
  977. +       }
  978.         stat_errno = errno;
  979.     }
  980.  
  981. @@ -1859,11 +1884,12 @@
  982.             do_unlink(partialptr);
  983.             handle_partial_dir(partialptr, PDIR_DELETE);
  984.         }
  985. -       set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
  986. +       if (!is_ntstreams_file)
  987. +           set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
  988.         if (itemizing)
  989.             itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
  990.  #ifdef SUPPORT_HARD_LINKS
  991. -       if (preserve_hard_links && F_IS_HLINKED(file))
  992. +       if (preserve_hard_links && F_IS_HLINKED(file) && !is_ntstreams_file)
  993.             finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
  994.  #endif
  995.         if (remove_source_files != 1)
  996. @@ -1873,7 +1899,7 @@
  997.             send_msg_int(MSG_SUCCESS, ndx);
  998.         goto cleanup;
  999.     }
  1000. -
  1001. +  
  1002.     if (append_mode > 0 && sx.st.st_size >= F_LENGTH(file)) {
  1003.  #ifdef SUPPORT_HARD_LINKS
  1004.         if (F_IS_HLINKED(file))
  1005. @@ -1897,7 +1923,7 @@
  1006.         if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
  1007.             if (!(backupptr = get_backup_name(fname)))
  1008.                 goto cleanup;
  1009. -           if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
  1010. +           if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS, fname)))
  1011.                 goto pretend_missing;
  1012.             if (copy_file(fname, backupptr, -1, back_file->mode, 1) < 0) {
  1013.                 unmake_file(back_file);
  1014. @@ -1913,6 +1939,11 @@
  1015.         if (j >= 0) /* don't use changing file as future fuzzy basis */
  1016.             fuzzy_dirlist->files[j]->flags |= FLAG_FILE_SENT;
  1017.     }
  1018. +  
  1019. +   if (is_ntstreams_file) {
  1020. +       statret = real_ret = -1;
  1021. +       goto notify_others;
  1022. +   }
  1023.  
  1024.     /* open the file */
  1025.     if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
  1026. @@ -1935,7 +1966,7 @@
  1027.             close(fd);
  1028.             goto cleanup;
  1029.         }
  1030. -       if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
  1031. +       if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS, fname))) {
  1032.             close(fd);
  1033.             goto pretend_missing;
  1034.         }
  1035. @@ -1966,6 +1997,57 @@
  1036.         fnamecmp_type = FNAMECMP_BACKUP;
  1037.     }
  1038.  
  1039. +   if (fd != -1 && basis_filter)
  1040. +   {
  1041. +       char tmp[MAXPATHLEN];
  1042. +       int fd_basis_filter = open_tmpfile(tmp, fnamecmp, file);
  1043. +
  1044. +       /* make sure filter argv is set up */
  1045. +       if (! filter_argv[0])
  1046. +           tokenize_filter_args(filter_argv, basis_filter, "basis");
  1047. +
  1048. +       if (fd_basis_filter == -1) {
  1049. +           rprintf(FERROR,
  1050. +               "could not open temporary file for %s; bypassing basis filter %s\n",
  1051. +               fname, basis_filter);
  1052. +       } else {
  1053. +           int status;
  1054. +           pid_t pid;
  1055. +           char *filter_argv_subst[MAX_FILTER_ARGS + 1];
  1056. +           enum filter_args_info fai = substitute_filter_args(filter_argv_subst, fnamecmp, tmp, filter_argv);
  1057. +           if (fai & fai_has_source) {
  1058. +               close(fd);
  1059. +               fd = -1;
  1060. +           }
  1061. +           if (fai & fai_has_dest) {
  1062. +               close(fd_basis_filter);
  1063. +               fd_basis_filter = -1;
  1064. +           }
  1065. +           pid = run_filter_on_file(filter_argv_subst, fd_basis_filter, fd);
  1066. +           if (fd >= 0) close(fd);
  1067. +           if (fd_basis_filter >= 0) close(fd_basis_filter);
  1068. +           wait_process_with_flush(pid, &status);
  1069. +           if (status != 0) {
  1070. +               rprintf(FERROR,
  1071. +                   "bypassing basis filter %s; exited with code: %d\n",
  1072. +                   basis_filter, status);
  1073. +               fd = do_open(fnamecmp, O_RDONLY, 0);
  1074. +           } else {
  1075. +               fd = do_open(tmp, O_RDONLY, 0);
  1076. +               fnamecmp = tmp;
  1077. +               if (do_fstat(fd,&sx.st) != 0) {
  1078. +                   rsyserr(FERROR_XFER, errno, "fstat %s failed",
  1079. +                       full_fname(fnamecmp));
  1080. +                   close(fd);
  1081. +                   goto cleanup;
  1082. +               }
  1083. +           }
  1084. +           fnamecmp_type = FNAMECMP_FILTERED;
  1085. +       }
  1086. +   }
  1087. +
  1088. +
  1089. +
  1090.     if (verbose > 3) {
  1091.         rprintf(FINFO, "gen mapped %s of size %.0f\n",
  1092.             fnamecmp, (double)sx.st.st_size);
  1093. @@ -1990,10 +2072,14 @@
  1094.             iflags |= ITEM_REPORT_CHANGE;
  1095.         if (fnamecmp_type != FNAMECMP_FNAME)
  1096.             iflags |= ITEM_BASIS_TYPE_FOLLOWS;
  1097. -       if (fnamecmp_type == FNAMECMP_FUZZY)
  1098. +       if (fnamecmp_type == FNAMECMP_FUZZY || fnamecmp_type == FNAMECMP_FILTERED)
  1099.             iflags |= ITEM_XNAME_FOLLOWS;
  1100.         itemize(fnamecmp, file, -1, real_ret, &real_sx, iflags, fnamecmp_type,
  1101. -           fuzzy_file ? fuzzy_file->basename : NULL);
  1102. +           fuzzy_file
  1103. +               ? fuzzy_file->basename
  1104. +               : fnamecmp_type == FNAMECMP_FILTERED
  1105. +                       ? fnamecmp
  1106. +                   : NULL);
  1107.  #ifdef SUPPORT_ACLS
  1108.         if (preserve_acls)
  1109.             free_acl(&real_sx);
  1110. @@ -2274,6 +2360,9 @@
  1111.  
  1112.     dflt_perms = (ACCESSPERMS & ~orig_umask);
  1113.  
  1114. +   if (basis_filter)
  1115. +       tokenize_filter_args(filter_argv, basis_filter, "basis");
  1116. +
  1117.     do {
  1118.  #ifdef SUPPORT_HARD_LINKS
  1119.         if (preserve_hard_links && inc_recurse) {
  1120. Index: main.c
  1121. ===================================================================
  1122. --- main.c  (revision 2)
  1123. +++ main.c  (working copy)
  1124. @@ -79,6 +79,8 @@
  1125.  extern char curr_dir[MAXPATHLEN];
  1126.  extern struct file_list *first_flist;
  1127.  extern struct filter_list_struct daemon_filter_list;
  1128. +extern char *file_size_cache;
  1129. +extern char *obfuscation_file;
  1130.  
  1131.  uid_t our_uid;
  1132.  int am_receiver = 0;  /* Only set to 1 after the receiver/generator fork. */
  1133. @@ -138,7 +140,7 @@
  1134.  }
  1135.  
  1136.  /* Wait for a process to exit, calling io_flush while waiting. */
  1137. -static void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
  1138. +void wait_process_with_flush(pid_t pid, int *exit_code_ptr)
  1139.  {
  1140.     pid_t waited_pid;
  1141.     int status;
  1142. @@ -957,6 +959,22 @@
  1143.  
  1144.  int child_main(int argc, char *argv[])
  1145.  {
  1146. +   /* child_main acts as the server and receiver for a local transfer.
  1147. +    * We need to set obfuscation_file to NULL so that the receiver does not
  1148. +    * immediately de-obfuscate file names that have just been obfuscated by the
  1149. +    * sender.
  1150. +    * Note:
  1151. +    * The current implementation of file name obfuscation (via the
  1152. +    * --obfuscation-file option) assumes that the
  1153. +    * obfuscation/deobfuscation operation is done on the client and that
  1154. +    * whether we are obfuscating or deobfuscating depends on whether the
  1155. +    * client is the sender or receiver.  This does not allow
  1156. +    * deobfuscation on local transfers where the "client" is always the
  1157. +    * sender.  If we wanted to support that then we would need to add a
  1158. +    * new option to differentiate between obfuscation and deobfuscation
  1159. +    * operations. */
  1160. +   obfuscation_file = NULL;
  1161. +
  1162.     start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
  1163.     return 0;
  1164.  }
  1165. @@ -1136,7 +1154,13 @@
  1166.         if (path) { /* source is remote */
  1167.             char *dummy_host;
  1168.             int dummy_port = 0;
  1169. -           *argv = path;
  1170. +           if (obfuscation_file && strstr(path, OBF_ARGPATH_START_MARKER)) {
  1171. +               char tmp_buf[MAXPATHLEN];
  1172. +               obfuscate_argpath(tmp_buf, MAXPATHLEN, path);
  1173. +               *argv = strdup(tmp_buf);
  1174. +           } else {
  1175. +               *argv = path;
  1176. +           }
  1177.             remote_argv = argv;
  1178.             remote_argc = argc;
  1179.             argv += argc - 1;
  1180. @@ -1238,7 +1262,13 @@
  1181.                     rprintf(FERROR, "All source args must use the same port number.\n");
  1182.                 exit_cleanup(RERR_SYNTAX);
  1183.             }
  1184. -           remote_argv[i] = arg;
  1185. +           if (obfuscation_file && strstr(arg, OBF_ARGPATH_START_MARKER)) {
  1186. +               char tmp_buf[MAXPATHLEN];
  1187. +               obfuscate_argpath(tmp_buf, MAXPATHLEN, arg);
  1188. +               remote_argv[i] = strdup(tmp_buf);
  1189. +           } else {
  1190. +               remote_argv[i] = arg;
  1191. +           }
  1192.         }
  1193.     }
  1194.  
  1195. @@ -1457,6 +1487,12 @@
  1196.  
  1197.     init_flist();
  1198.  
  1199. +   if (file_size_cache)
  1200. +       fscache_load();
  1201. +
  1202. +   if (obfuscation_file)
  1203. +       obf_load();
  1204. +
  1205.     if ((write_batch || read_batch) && !am_server) {
  1206.         if (write_batch)
  1207.             write_batch_shell_file(orig_argc, orig_argv, argc);
  1208. Index: Makefile.in
  1209. ===================================================================
  1210. --- Makefile.in (revision 2)
  1211. +++ Makefile.in (working copy)
  1212. @@ -35,7 +35,8 @@
  1213.  OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
  1214.     util.o main.o checksum.o match.o syscall.o log.o backup.o
  1215.  OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
  1216. -   fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
  1217. +   fileio.o batch.o clientname.o chmod.o acls.o xattrs.o fscache.o ntstreams.o  \
  1218. +   obfuscation.o
  1219.  OBJS3=progress.o pipe.o
  1220.  DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
  1221.  popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
  1222. Index: ntstreams.c
  1223. ===================================================================
  1224. --- ntstreams.c (revision 0)
  1225. +++ ntstreams.c (revision 0)
  1226. @@ -0,0 +1,528 @@
  1227. +/*
  1228. + * NTFS backup/restore routines
  1229. + *
  1230. + * Copyright (C) 2010 Cortex IT
  1231. + *
  1232. + * This program is free software; you can redistribute it and/or modify
  1233. + * it under the terms of the GNU General Public License as published by
  1234. + * the Free Software Foundation; either version 3 of the License, or
  1235. + * (at your option) any later version.
  1236. + *
  1237. + * This program is distributed in the hope that it will be useful,
  1238. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1239. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1240. + * GNU General Public License for more details.
  1241. + *
  1242. + * You should have received a copy of the GNU General Public License along
  1243. + * with this program; if not, visit the http://fsf.org website.
  1244. + */
  1245. +
  1246. +#include "rsync.h"
  1247. +#include <windows.h>
  1248. +#include <sys/cygwin.h>
  1249. +
  1250. +#define NTSTREAMS_DIR ".nt_streams"
  1251. +#define BUFFER_SIZE (64*1024)
  1252. +#define ERROR_BUFFER_SIZE 1024
  1253. +
  1254. +extern int verbose;
  1255. +
  1256. +static char ntstreams_last_error[ERROR_BUFFER_SIZE];
  1257. +static char *ntstreams_last_error_what = "";
  1258. +
  1259. +enum hopen_mode { hopen_read, hopen_write };
  1260. +
  1261. +static HANDLE hopen(const char *path, enum hopen_mode mode)
  1262. +{
  1263. +   DWORD desiredAccess, shareMode, creationDisposition, flagsAndAttributes;
  1264. +   LPSECURITY_ATTRIBUTES securityAttributes = NULL;
  1265. +   HANDLE h, templateFile = NULL;
  1266. +   wchar_t *win_path = (wchar_t *)cygwin_create_path(CCP_POSIX_TO_WIN_W, (void *)path);
  1267. +   if (verbose > 3)
  1268. +       rprintf(FINFO, "cygwin_create_path(%s) = %ls\n", path, win_path);
  1269. +   switch (mode) {
  1270. +       case hopen_read:
  1271. +           desiredAccess = GENERIC_READ | ACCESS_SYSTEM_SECURITY;
  1272. +           shareMode = FILE_SHARE_READ;
  1273. +           creationDisposition = OPEN_EXISTING;
  1274. +           flagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
  1275. +           break;
  1276. +       case hopen_write:
  1277. +       default:
  1278. +           /* desiredAccess = GENERIC_ALL | ACCESS_SYSTEM_SECURITY;
  1279. +            * GENERIC_ALL causes CreateFileW to give an "access denied" error.
  1280. +            * I don't know why, since this works in CortexIT.Replicator.BackupStreams.
  1281. +            */
  1282. +           desiredAccess = WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY;
  1283. +           shareMode = FILE_SHARE_WRITE;
  1284. +           creationDisposition = OPEN_EXISTING;
  1285. +           flagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS;
  1286. +           break;
  1287. +   }
  1288. +   h = CreateFileW(win_path, desiredAccess, shareMode, securityAttributes, creationDisposition, flagsAndAttributes, templateFile);
  1289. +   free(win_path);
  1290. +   return h;
  1291. +}
  1292. +
  1293. +// Size of the stream header without the stream name (which we treat seperately).
  1294. +// #define SIZEOF_WIN32_STREAM_ID (sizeof(WIN32_STREAM_ID) - sizeof(WCHAR[ANYSIZE_ARRAY]))
  1295. +#define SIZEOF_WIN32_STREAM_ID 20
  1296. +
  1297. +/* Return values:
  1298. + * 1: success
  1299. + * 0: Posix error (error code in errno)
  1300. + * -1: win32 error (use GetLastError to get error code)*/
  1301. +int ntfs_streams_to_fd(const char *path, int fd)
  1302. +{
  1303. +   int success = 1;
  1304. +   LPVOID context = NULL;
  1305. +   char buffer[BUFFER_SIZE];
  1306. +   WIN32_STREAM_ID header;
  1307. +   DWORD bytesRead;
  1308. +   HANDLE h = hopen(path, hopen_read);
  1309. +   if (h == INVALID_HANDLE_VALUE) {
  1310. +       DWORD lastError = GetLastError();
  1311. +       ntstreams_last_error_what = "CreateFile";
  1312. +       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1313. +           NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1314. +               ntstreams_last_error, ERROR_BUFFER_SIZE, NULL);
  1315. +       return -1;
  1316. +   }
  1317. +
  1318. +   while (1) {
  1319. +       LARGE_INTEGER bytesRemaining;
  1320. +       if (!BackupRead(h, (LPBYTE)(&header), SIZEOF_WIN32_STREAM_ID, &bytesRead, 0, 1, &context)) {
  1321. +           success = -1;
  1322. +           ntstreams_last_error_what = "BackupRead";
  1323. +           break;
  1324. +       }
  1325. +       if (bytesRead == 0 || header.dwStreamId == 0)
  1326. +           break; /* end of file */
  1327. +
  1328. +       if (verbose > 3)
  1329. +           rprintf(FINFO, "BackupRead found header type %lx, size %lld, streamNameSize %ld, bytesRead %ld\n", header.dwStreamId, header.Size.QuadPart, header.dwStreamNameSize, bytesRead);
  1330. +
  1331. +       bytesRemaining.QuadPart = SIZEOF_WIN32_STREAM_ID + header.Size.QuadPart + header.dwStreamNameSize - bytesRead;
  1332. +       if (header.dwStreamId == BACKUP_DATA) {
  1333. +           DWORD lowBytesSeeked, highBytesSeeked;
  1334. +
  1335. +           /* Seek over the rest of the stream */
  1336. +           if (!BackupSeek(h, bytesRemaining.LowPart, bytesRemaining.HighPart, &lowBytesSeeked, &highBytesSeeked, &context)) {
  1337. +               success = -1;
  1338. +               ntstreams_last_error_what = "BackupSeek";
  1339. +               break;
  1340. +           }
  1341. +       } else {
  1342. +           if (write(fd, &header, SIZEOF_WIN32_STREAM_ID) < 0) {
  1343. +               success = 0;
  1344. +               ntstreams_last_error_what = "write";
  1345. +               break;
  1346. +           }
  1347. +           while(bytesRemaining.QuadPart > 0) {
  1348. +               DWORD count = bytesRemaining.QuadPart > BUFFER_SIZE ? BUFFER_SIZE : bytesRemaining.LowPart;
  1349. +               if (!BackupRead(h, (LPBYTE)buffer, count, &bytesRead, 0, 1, &context)) {
  1350. +                   success = -1;
  1351. +                   ntstreams_last_error_what = "BackupRead";
  1352. +                   goto finish;
  1353. +               }
  1354. +               if (verbose > 3)
  1355. +                   rprintf(FINFO, "BackupRead count %ld, bytesRead %ld\n", count, bytesRead);
  1356. +               if (bytesRead == 0)
  1357. +                   break;
  1358. +               if (write(fd, buffer, bytesRead) < 0) {
  1359. +                   success = 0;
  1360. +                   ntstreams_last_error_what = "write";
  1361. +                   goto finish;
  1362. +               }
  1363. +               bytesRemaining.QuadPart -= bytesRead;
  1364. +           }
  1365. +       }
  1366. +   }
  1367. +finish:
  1368. +   if (success == 0) {
  1369. +       /* Posix error */
  1370. +       strncpy(ntstreams_last_error, strerror(errno), ERROR_BUFFER_SIZE);
  1371. +   } else if (success == -1) {
  1372. +       /* win32 error */
  1373. +       DWORD lastError = GetLastError();
  1374. +       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1375. +           NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1376. +               ntstreams_last_error, ERROR_BUFFER_SIZE, NULL);
  1377. +   }
  1378. +   BackupRead(h, NULL, 0, &bytesRead, 1, 1, &context);
  1379. +   CloseHandle(h);
  1380. +   return success;
  1381. +}
  1382. +
  1383. +int fd_to_ntfs_streams(int fd, const char *path)
  1384. +{
  1385. +   int success = 1;
  1386. +   LPVOID context = NULL;
  1387. +   char buffer[BUFFER_SIZE];
  1388. +   WIN32_STREAM_ID header;
  1389. +   ssize_t bytesRead;
  1390. +   DWORD bytesWritten;
  1391. +   HANDLE h = hopen(path, hopen_write);
  1392. +   if (h == INVALID_HANDLE_VALUE) {
  1393. +       DWORD lastError = GetLastError();
  1394. +       ntstreams_last_error_what = "CreateFile";
  1395. +       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1396. +           NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1397. +               ntstreams_last_error, ERROR_BUFFER_SIZE, NULL);
  1398. +       return -1;
  1399. +   }
  1400. +
  1401. +   while (1) {
  1402. +       if ((bytesRead = read(fd, (void *)(&header), SIZEOF_WIN32_STREAM_ID)) < 0) {
  1403. +           success = 0;
  1404. +           ntstreams_last_error_what = "read";
  1405. +           break;
  1406. +       }
  1407. +       if (bytesRead == 0 || header.dwStreamId == 0)
  1408. +           break; /* end of file */
  1409. +
  1410. +       if (header.dwStreamId == BACKUP_DATA) {
  1411. +           /* Seek over header name */
  1412. +           if (header.dwStreamNameSize > 0) {
  1413. +               if (lseek(fd, header.dwStreamNameSize, SEEK_CUR) < 0) {
  1414. +                   success = 0;
  1415. +                   ntstreams_last_error_what = "lseek";
  1416. +                   break;
  1417. +               }
  1418. +           }
  1419. +
  1420. +           /* Seek over the rest of the stream */
  1421. +           if (lseek(fd, header.Size.QuadPart, SEEK_CUR) < 0) {
  1422. +               success = 0;
  1423. +               ntstreams_last_error_what = "lseek";
  1424. +               break;
  1425. +           }
  1426. +       } else {
  1427. +           int64 bytesRemaining;
  1428. +           if (!BackupWrite(h, (LPBYTE)&header, SIZEOF_WIN32_STREAM_ID, &bytesWritten, 0, 1, &context)) {
  1429. +               success = -1;
  1430. +               ntstreams_last_error_what = "BackupWrite (header)";
  1431. +               break;
  1432. +           }
  1433. +           bytesRemaining = header.Size.QuadPart + header.dwStreamNameSize;
  1434. +           while(bytesRemaining > 0) {
  1435. +               size_t count = bytesRemaining > BUFFER_SIZE ? BUFFER_SIZE : (size_t)bytesRemaining;
  1436. +               if ((bytesRead = read(fd, (void *)buffer, count)) < 0) {
  1437. +                   success = 0;
  1438. +                   ntstreams_last_error_what = "read";
  1439. +                   goto finish;
  1440. +               }
  1441. +               if (bytesRead == 0)
  1442. +                   goto finish; /* end of file */
  1443. +               if (!BackupWrite(h, (LPBYTE)buffer, (DWORD)count, &bytesWritten, 0, 1, &context)) {
  1444. +                   success = -1;
  1445. +                   ntstreams_last_error_what = "BackupWrite (data)";
  1446. +                   goto finish;
  1447. +               }
  1448. +               bytesRemaining -= count;
  1449. +           }
  1450. +       }
  1451. +   }
  1452. +finish:
  1453. +   if (success == 0) {
  1454. +       /* Posix error */
  1455. +       strncpy(ntstreams_last_error, strerror(errno), ERROR_BUFFER_SIZE);
  1456. +   } else if (success == -1) {
  1457. +       /* win32 error */
  1458. +       DWORD lastError = GetLastError();
  1459. +       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1460. +           NULL, lastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1461. +               ntstreams_last_error, ERROR_BUFFER_SIZE, NULL);
  1462. +   }
  1463. +   BackupWrite(h, NULL, 0, &bytesWritten, 1, 1, &context);
  1464. +   CloseHandle(h);
  1465. +   return success;
  1466. +}
  1467. +
  1468. +void ntstreams_dirname(char *dest, size_t destsize, const char *dirname)
  1469. +{
  1470. +   if (!dirname || strcmp(dirname, ".") == 0)
  1471. +       strncpy(dest, NTSTREAMS_DIR, destsize);
  1472. +   else
  1473. +       pathjoin(dest, destsize, dirname, NTSTREAMS_DIR);
  1474. +}
  1475. +
  1476. +size_t ntstreams_filename(char *dest, char *orig, size_t destsize, const char *dirname, const char *basename)
  1477. +{
  1478. +   char tmp[MAXPATHLEN];
  1479. +   if (!dirname)
  1480. +          dirname = ".";
  1481. +   ntstreams_dirname(tmp, MAXPATHLEN, dirname);
  1482. +   if (strcmp(dirname, ".") == 0)
  1483. +       strncpy(orig, basename, destsize);
  1484. +   else
  1485. +       pathjoin(orig, destsize, dirname, basename);
  1486. +   return pathjoin(dest, destsize, tmp, basename);
  1487. +}
  1488. +
  1489. +int ntstreams_orig_filename(char *dest, size_t destsize, const char *dirname, const char *basename)
  1490. +{
  1491. +   char *p, tmp[MAXPATHLEN];
  1492. +   int ret = 0;
  1493. +   strncpy(tmp, dirname ? dirname : ".", MAXPATHLEN);
  1494. +   p = strstr(tmp, NTSTREAMS_DIR);
  1495. +   if (p) {
  1496. +       *p = '\0';
  1497. +       ret = 1;
  1498. +   }
  1499. +   if (strlen(tmp) == 0 || strcmp(tmp, ".") == 0)
  1500. +       strncpy(dest, basename, destsize);
  1501. +   else
  1502. +       pathjoin(dest, destsize, tmp, basename);
  1503. +   return ret;
  1504. +}
  1505. +
  1506. +char *ntstreams_get_last_error_message()
  1507. +{
  1508. +   return ntstreams_last_error;
  1509. +}
  1510. +
  1511. +const char *ntstreams_get_last_error_what()
  1512. +{
  1513. +   return ntstreams_last_error_what;
  1514. +}
  1515. +
  1516. +int set_privilege_backup(int bEnablePrivilege)
  1517. +{
  1518. +   return set_privilege(SE_BACKUP_NAME, bEnablePrivilege);
  1519. +}
  1520. +
  1521. +int set_privilege_restore(int bEnablePrivilege)
  1522. +{
  1523. +   return set_privilege(SE_RESTORE_NAME, bEnablePrivilege);
  1524. +}
  1525. +
  1526. +int set_privilege_security(int bEnablePrivilege)
  1527. +{
  1528. +   return set_privilege(SE_SECURITY_NAME, bEnablePrivilege);
  1529. +}
  1530. +
  1531. +int set_privilege_take_ownership(int bEnablePrivilege)
  1532. +{
  1533. +   return set_privilege(SE_TAKE_OWNERSHIP_NAME, bEnablePrivilege);
  1534. +}
  1535. +
  1536. +int set_privilege(const char *lpszPrivilege, int bEnablePrivilege)
  1537. +{
  1538. +   HANDLE hToken;
  1539. +   TOKEN_PRIVILEGES tp;
  1540. +   LUID luid;
  1541. +
  1542. +   if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
  1543. +       goto handle_error;
  1544. +
  1545. +   if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid))
  1546. +       goto handle_error;
  1547. +
  1548. +   tp.PrivilegeCount = 1;
  1549. +   tp.Privileges[0].Luid = luid;
  1550. +   if (bEnablePrivilege)
  1551. +       tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  1552. +   else
  1553. +       tp.Privileges[0].Attributes = 0;
  1554. +
  1555. +   if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL))
  1556. +       goto handle_error;
  1557. +
  1558. +   if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
  1559. +       goto handle_error;
  1560. +   return TRUE;
  1561. +
  1562. +handle_error:
  1563. +   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  1564. +       NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  1565. +       ntstreams_last_error, ERROR_BUFFER_SIZE, NULL);
  1566. +   return FALSE;
  1567. +}
  1568. +
  1569. +struct restore_list {
  1570. +   char *basename;
  1571. +   time_t modtime;
  1572. +   mode_t mode;
  1573. +   struct restore_list *next;
  1574. +};
  1575. +
  1576. +static void cleanup_restore_list(struct restore_list *list)
  1577. +{
  1578. +   while (list) {
  1579. +       struct restore_list *p = list;
  1580. +       list = list->next;
  1581. +       free(p->basename);
  1582. +       free(p);
  1583. +   }
  1584. +}
  1585. +
  1586. +struct restore_stack_node {
  1587. +   char *dirname;
  1588. +   struct restore_list *restore_list;
  1589. +   struct restore_stack_node *next;
  1590. +};
  1591. +
  1592. +struct restore_stack {
  1593. +   struct restore_stack_node* top;
  1594. +};
  1595. +
  1596. +struct restore_stack restore_stack;
  1597. +
  1598. +static void push(struct restore_stack *stack, const char *dirname)
  1599. +{
  1600. +   struct restore_stack_node *node = new(struct restore_stack_node);
  1601. +   if (verbose > 3)
  1602. +       rprintf(FINFO, "ntstreams push %s\n", dirname);
  1603. +   node->dirname = strdup(dirname);
  1604. +   node->restore_list = NULL;
  1605. +   node->next = stack->top;
  1606. +   stack->top = node;
  1607. +}
  1608. +
  1609. +static void cleanup_restore_stack_node(struct restore_stack_node *node)
  1610. +{
  1611. +   if (!node)
  1612. +       return;
  1613. +   free(node->dirname);
  1614. +   cleanup_restore_list(node->restore_list);
  1615. +   free(node);
  1616. +}
  1617. +
  1618. +static void pop(struct restore_stack *stack)
  1619. +{
  1620. +   if (stack->top) {
  1621. +       struct restore_stack_node *p = stack->top;
  1622. +       if (verbose > 3)
  1623. +           rprintf(FINFO, "ntstreams pop %s\n", stack->top->dirname);
  1624. +       stack->top = p->next;
  1625. +       cleanup_restore_stack_node(p);
  1626. +   }
  1627. +}
  1628. +
  1629. +static void add_fname(struct restore_stack *stack, const struct file_struct *file)
  1630. +{
  1631. +   if (!stack->top)
  1632. +       return;
  1633. +   struct restore_list *list_node = new(struct restore_list);
  1634. +   list_node->basename = strdup(file->basename);
  1635. +   list_node->modtime = file->modtime;
  1636. +   list_node->mode = file->mode;
  1637. +   list_node->next = stack->top->restore_list;
  1638. +   stack->top->restore_list = list_node;
  1639. +}
  1640. +
  1641. +static void restore_streams_and_remove(const char *dirname, const char *basename, time_t modtime, mode_t mode)
  1642. +{
  1643. +   char streams_fname[MAXPATHLEN], orig_fname[MAXPATHLEN];
  1644. +   int fd;
  1645. +   ntstreams_filename(streams_fname, orig_fname, MAXPATHLEN, dirname, basename);
  1646. +
  1647. +   /* Can't restore streams for "." without knowing its real name. */
  1648. +   if (strcmp(orig_fname, ".") == 0)
  1649. +       return;
  1650. +
  1651. +   fd = do_open(streams_fname, O_RDONLY, 0);
  1652. +   if (fd == -1) {
  1653. +       /* If the file was unchanged then there won't be a corresponding streams file.
  1654. +        * Therefore we need to ignore any errors with opening the streams file.
  1655. +        * rsyserr(FERROR, errno, "restore_streams_and_remove: open failed for %s", full_fname(streams_fname));
  1656. +        */
  1657. +       return;
  1658. +   }
  1659. +   if (verbose > 2) {
  1660. +       rprintf(FINFO, "restoring NT streams for %s\n", orig_fname);
  1661. +   }
  1662. +   if (fd_to_ntfs_streams(fd, orig_fname) <= 0) {
  1663. +       rprintf(FERROR, "fd_to_ntfs_streams %s failed on %s, skipping nt streams: %s\n",
  1664. +           ntstreams_get_last_error_what(), orig_fname, ntstreams_get_last_error_message());
  1665. +       close(fd);
  1666. +       return;
  1667. +   }
  1668. +   close(fd);
  1669. +   if (unlink(streams_fname) == -1) {
  1670. +       rsyserr(FERROR, errno, "restore_streams_and_remove: unlink failed for %s", full_fname(streams_fname));
  1671. +   }
  1672. +   if (set_modtime(orig_fname, modtime, mode) != 0)
  1673. +   {
  1674. +       rsyserr(FERROR, errno, "restore_streams_and_remove: set_modtime failed for %s", full_fname(orig_fname));
  1675. +   }
  1676. +}
  1677. +
  1678. +static void remove_ntstreams_dir(const char *dirname)
  1679. +{
  1680. +   char streams_dir[MAXPATHLEN];
  1681. +   ntstreams_dirname(streams_dir, MAXPATHLEN, dirname);
  1682. +   if (rmdir(streams_dir) == -1) {
  1683. +       /* Don't report the error because the directory may not exist anyway.
  1684. +        * rsyserr(FERROR, errno, "remove_ntstreams_dir: rmdir failed for %s", full_fname(streams_dir));
  1685. +        */
  1686. +   }
  1687. +}
  1688. +
  1689. +static int is_subdir_or_eq(const char *p1, const char *p2)
  1690. +{
  1691. +   int len = strlen(p2);
  1692. +   return strstr(p1, p2) == p1 && (p1[len] == '\0'  || p1[len] == '/');
  1693. +}
  1694. +
  1695. +static int is_ntstreams_dir_or_file(const char *s)
  1696. +{
  1697. +   char *p = strstr(s, NTSTREAMS_DIR);
  1698. +   if (!p)
  1699. +       return 0;
  1700. +   p += strlen(NTSTREAMS_DIR);
  1701. +   return *p == '/' || *p == '\0';
  1702. +}
  1703. +
  1704. +void ntstreams_restore_update(const char *fname, const struct file_struct *file)
  1705. +{
  1706. +   static const char *dotdir = ".";
  1707. +   struct restore_stack *stack = &restore_stack;
  1708. +   if (fname && is_ntstreams_dir_or_file(fname))
  1709. +       return;
  1710. +
  1711. +   if (!stack->top)
  1712. +       push(stack, dotdir);
  1713. +
  1714. +   const char *dirname = file && file->dirname ? file->dirname : dotdir;
  1715. +   while (stack->top && (!fname || (strcmp(stack->top->dirname, dotdir) != 0 && !is_subdir_or_eq(dirname, stack->top->dirname))))
  1716. +   {
  1717. +       struct restore_list *rl;
  1718. +       for(rl = stack->top->restore_list; rl; rl = rl->next) {
  1719. +           restore_streams_and_remove(stack->top->dirname, rl->basename, rl->modtime, rl->mode);
  1720. +       }
  1721. +       remove_ntstreams_dir(stack->top->dirname);
  1722. +       pop(stack);
  1723. +   }
  1724. +
  1725. +   // The stack is empty so we're done.
  1726. +   if (!stack->top)
  1727. +       return;
  1728. +
  1729. +   if (dirname != dotdir) {
  1730. +       // Top of stack is now a parent directory (or equal to) dirname.
  1731. +       // Now push each component of dirname onto the stack.
  1732. +       char buf[MAXPATHLEN], *b = buf;
  1733. +       if (strcmp(stack->top->dirname, dotdir) != 0) {
  1734. +           strncpy(buf, stack->top->dirname, MAXPATHLEN);
  1735. +           b += strlen(b);
  1736. +           *b++ = '/';
  1737. +       }
  1738. +       const char *p = dirname + (strcmp(stack->top->dirname, dotdir) == 0 ? 0 : strlen(stack->top->dirname));
  1739. +       if (*p == '/')
  1740. +           p++;
  1741. +       while (*p) {
  1742. +           do {
  1743. +               *b++ = *p++;
  1744. +           }
  1745. +           while (*p && *p != '/');
  1746. +           *b = '\0';
  1747. +           push(stack, buf);
  1748. +       }
  1749. +   }
  1750. +
  1751. +   if (!fname)
  1752. +       return;
  1753. +   add_fname(stack, file);
  1754. +}
  1755. Index: obfuscation.c
  1756. ===================================================================
  1757. --- obfuscation.c   (revision 0)
  1758. +++ obfuscation.c   (revision 0)
  1759. @@ -0,0 +1,446 @@
  1760. +/*
  1761. + * File name obfuscation
  1762. + *
  1763. + * Copyright (C) 2010 Cortex IT
  1764. + *
  1765. + * This program is free software; you can redistribute it and/or modify
  1766. + * it under the terms of the GNU General Public License as published by
  1767. + * the Free Software Foundation; either version 3 of the License, or
  1768. + * (at your option) any later version.
  1769. + *
  1770. + * This program is distributed in the hope that it will be useful,
  1771. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  1772. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  1773. + * GNU General Public License for more details.
  1774. + *
  1775. + * You should have received a copy of the GNU General Public License along
  1776. + * with this program; if not, visit the http://fsf.org website.
  1777. + */
  1778. +
  1779. +/* WARNING:
  1780. + * This module assumes that the local file system is case-insensitive (e.g. NTFS or FAT).
  1781. + * If you want to use it on a case-sensitive file system then you should change
  1782. + * strcasecmp calls to strcmp.
  1783. + */
  1784. +
  1785. +#include "rsync.h"
  1786. +
  1787. +/* Increment this if you change the file format */
  1788. +#define OBF_VERSION 1
  1789. +
  1790. +#define OBF_KEY_LENGTH 8
  1791. +#define OBF_CHARS_LENGTH 37
  1792. +
  1793. +#define OBF_TABLE_SIZE 65537 /* prime number */
  1794. +
  1795. +/* how long to keep obftable entries that have not been needed in this run */
  1796. +#define OBF_MAX_AGE 10
  1797. +
  1798. +extern char *obfuscation_file;
  1799. +extern int verbose;
  1800. +extern char *partial_dir;
  1801. +
  1802. +struct obf_node {
  1803. +   const char *key;
  1804. +   const char *value;
  1805. +   int age;
  1806. +   struct obf_node *next;
  1807. +};
  1808. +
  1809. +static struct obf_node *obf_table[OBF_TABLE_SIZE], *deobf_obf_table[OBF_TABLE_SIZE], *obf_dirname_table[OBF_TABLE_SIZE];
  1810. +
  1811. +static const char obf_chars[] = "_0123456789abcdefghijklmnopqrstuvwxyz";
  1812. +
  1813. +static int obf_table_changed = 0;
  1814. +
  1815. +/* return a random character from obf_chars.
  1816. + */
  1817. +static char obf(void)
  1818. +{
  1819. +   static int n = 0;
  1820. +   static int seeded = 0;
  1821. +   int m;
  1822. +
  1823. +   /* If this is the first call to obf() then
  1824. +    * seed the random number generator.
  1825. +    */
  1826. +   if (!seeded) {
  1827. +       srand(time(NULL));
  1828. +       seeded = 1;
  1829. +   }
  1830. +
  1831. +   if (n == 0)
  1832. +       n = rand();
  1833. +   m = n % OBF_CHARS_LENGTH;
  1834. +   n /= OBF_CHARS_LENGTH;
  1835. +   return obf_chars[m];
  1836. +}
  1837. +
  1838. +static void obfuscate(char *value)
  1839. +{
  1840. +   int i;
  1841. +   for (i = 0; i < OBF_KEY_LENGTH; ++i)
  1842. +       value[i] = obf();
  1843. +   value[OBF_KEY_LENGTH] = '\0';
  1844. +}
  1845. +
  1846. +static unsigned hash(const char *basename)
  1847. +{
  1848. +   unsigned n = 0;
  1849. +   const char *p;
  1850. +   for (p = basename; *p; ++p)
  1851. +       n = (64 * n + tolower((int)*p)) % OBF_TABLE_SIZE;
  1852. +       /* tolower() ensures that hashing is case-insensitive. */
  1853. +   return n;
  1854. +}
  1855. +
  1856. +/* Lookup is case insensitive.  NOTE: this will cause problems if deobfuscating to a file system where case is significant. */
  1857. +static const char *lookup_key(struct obf_node **table, const char *key)
  1858. +{
  1859. +   struct obf_node *p;
  1860. +   for (p = table[hash(key)]; p; p = p->next) {
  1861. +       int cmp = strcasecmp(p->key, key);
  1862. +       if (cmp == 0) {
  1863. +           p->age = 0; /* reset age */
  1864. +           return p->value;
  1865. +       }
  1866. +       if (cmp > 0)
  1867. +           return NULL;
  1868. +   }
  1869. +   return NULL;
  1870. +}
  1871. +
  1872. +static int contains_key(struct obf_node **table, const char *key)
  1873. +{
  1874. +   return lookup_key(table, key) != NULL;
  1875. +}
  1876. +
  1877. +static struct obf_node *new_node(const char *key, const char *value, int age, struct obf_node *next)
  1878. +{
  1879. +   struct obf_node *p = new(struct obf_node);
  1880. +   p->key = key;
  1881. +   p->value = value;
  1882. +   p->age = age;
  1883. +   p->next = next;
  1884. +   return p;
  1885. +}
  1886. +
  1887. +/* Insert the key if it's not there.  
  1888. + * Case is preserved, but comparison with existing keys is case-insensitive */
  1889. +static void insert_key_value(struct obf_node **table, const char *key, const char *value, int age)
  1890. +{
  1891. +   struct obf_node **p;
  1892. +   for (p = table + hash(key); *p; p = &(*p)->next) {
  1893. +       int cmp = strcasecmp((*p)->key, key);
  1894. +       if (cmp == 0)
  1895. +           return;
  1896. +       if (cmp > 0)
  1897. +           break;
  1898. +   }
  1899. +   *p = new_node(key, value, age, *p);
  1900. +}
  1901. +
  1902. +enum obf_flags { OBF_NONE = 0, OBF_ADD_IF_NOT_THERE = 1<<0, OBF_PATTERN_MODE = 1<<1, OBF_PARTIAL = 1<<2 };
  1903. +
  1904. +static const char *obfuscate_component(const char *component, enum obf_flags flags)
  1905. +{
  1906. +   const char *obf_component;
  1907. +
  1908. +   /* If we're in pattern mode and the pattern contains a wildcard character ('*' or '?')
  1909. +    * sending an obfuscated pattern is not going to work.  The best we can do is send
  1910. +    * "*" for this component, which may end up matching too much, but it's better than
  1911. +    * not matching anything.  If the pattern contains "**" then we need to preserve that.
  1912. +    */
  1913. +   if (flags & OBF_PATTERN_MODE && (strchr(component, '*') || strchr(component, '?'))) {
  1914. +       if (strstr(component, "**"))
  1915. +           return "**";
  1916. +       else
  1917. +           return "*";
  1918. +   }
  1919. +
  1920. +        obf_component = lookup_key(obf_table, component);
  1921. +   if (obf_component)
  1922. +       return obf_component;
  1923. +
  1924. +   if (!(flags & OBF_ADD_IF_NOT_THERE))
  1925. +       return component;
  1926. +
  1927. +   // Component is not already in the obf_table so create it.
  1928. +
  1929. +   obf_table_changed = 1;
  1930. +   char *s = new_array(char, OBF_KEY_LENGTH + 1);
  1931. +   char *c = strdup(component);
  1932. +   do {
  1933. +       obfuscate(s);
  1934. +   } while(contains_key(deobf_obf_table, s));
  1935. +
  1936. +   if (verbose > 2)
  1937. +       rprintf(FINFO, "obf inserting key %s, value %s\n", c, s);
  1938. +
  1939. +   insert_key_value(obf_table, c, s, 0);
  1940. +   insert_key_value(deobf_obf_table, s, c, 0);
  1941. +   return s;
  1942. +}
  1943. +
  1944. +static const char *deobfuscate_component(const char *obf_component, enum obf_flags flags)
  1945. +{
  1946. +   const char *s = lookup_key(deobf_obf_table, obf_component);
  1947. +   if (s)
  1948. +       /* deobfuscated component was found, so return it */
  1949. +       return s;
  1950. +   else
  1951. +       /* not found, so assume that it is already deobfuscated */
  1952. +       return obf_component;
  1953. +}
  1954. +
  1955. +static void convert(const char *(*f)(const char *, enum obf_flags), char *fname_out, int size, const char *fname_in, enum obf_flags flags)
  1956. +{
  1957. +   char *tmp = strdup(fname_in), *tmp_p = tmp, *out_p = fname_out;
  1958. +   int first = 1, startslash, endslash, obfuscate_now;
  1959. +
  1960. +   obfuscate_now = !(flags & OBF_PARTIAL);
  1961. +
  1962. +   /* Patterns may start with "+ " or "- ", which we need to pass through unobfuscated.
  1963. +    */
  1964. +   if (flags & OBF_PATTERN_MODE
  1965. +           && tmp_p
  1966. +           && strlen(tmp_p) >= 2
  1967. +           && (tmp_p[0] == '+' || tmp_p[0] == '-')
  1968. +           && tmp_p[1] == ' '
  1969. +           && size >= 2) {
  1970. +       *(out_p++) = tmp_p[0];
  1971. +       *(out_p++) = ' ';
  1972. +       size -= 2;
  1973. +       tmp_p += 2;
  1974. +   }
  1975. +   startslash = tmp_p[0] == '/';
  1976. +   endslash = tmp_p[strlen(tmp_p) - 1] == '/';
  1977. +
  1978. +   if (startslash && size > 0) {
  1979. +       *(out_p++) = '/';
  1980. +       --size;
  1981. +   }
  1982. +
  1983. +   for (tmp_p = strtok(tmp_p, "/"); tmp_p && size > 0; tmp_p = strtok(NULL, "/")) {
  1984. +
  1985. +       /* If in partial mode then check whether we've found the special marker "!!!" which
  1986. +        * tells us that it's time to start obfuscating */
  1987. +       if (flags & OBF_PARTIAL && !obfuscate_now && strcasecmp(tmp_p, OBF_ARGPATH_START_MARKER) == 0) {
  1988. +           obfuscate_now = 1;
  1989. +           continue;
  1990. +       }
  1991. +
  1992. +       const char *conv = obfuscate_now ? f(tmp_p, flags) : tmp_p;
  1993. +       int len = strlen(conv);
  1994. +       if (!first) {
  1995. +           *(out_p++) = '/';
  1996. +           --size;
  1997. +       }
  1998. +       strncpy(out_p, conv, size);
  1999. +       out_p += len;
  2000. +       size -= len;
  2001. +       first = 0;
  2002. +   }
  2003. +   if (endslash && size > 0)
  2004. +       *(out_p++) = '/';
  2005. +
  2006. +   *out_p = '\0';
  2007. +   free(tmp);
  2008. +}
  2009. +
  2010. +static void obfuscate_fname(char *fname_obf, int size, const char *fname)
  2011. +{
  2012. +   convert(obfuscate_component, fname_obf, size, fname, OBF_ADD_IF_NOT_THERE);
  2013. +}
  2014. +
  2015. +void deobfuscate_fname(char *fname, int size, const char *fname_obf)
  2016. +{
  2017. +   convert(deobfuscate_component, fname, size, fname_obf, OBF_NONE);
  2018. +}
  2019. +
  2020. +void obfuscate_pattern(char *pat_obf, int size, const char *pat)
  2021. +{
  2022. +   convert(obfuscate_component, pat_obf, size, pat, OBF_PATTERN_MODE | OBF_ADD_IF_NOT_THERE);
  2023. +}
  2024. +
  2025. +void obfuscate_argpath(char *argpath_obf, int size, const char *argpath)
  2026. +{
  2027. +   convert(obfuscate_component, argpath_obf, size, argpath, OBF_PARTIAL);
  2028. +   if (verbose > 2)
  2029. +       rprintf(FINFO, "obfuscate_argpath(%s) = %s\n", argpath, argpath_obf);
  2030. +}
  2031. +
  2032. +const char *obf_lookup_basename(const char *component)
  2033. +{
  2034. +   const char *obf_component = obfuscate_component(component, OBF_ADD_IF_NOT_THERE);
  2035. +   if (verbose > 2)
  2036. +       rprintf(FINFO, "obf_lookup_basename(%s) = %s\n", component, obf_component);
  2037. +   return obf_component;
  2038. +}
  2039. +
  2040. +const char *obf_lookup_dirname(const char *dirname)
  2041. +{
  2042. +   const char *obf_dirname = lookup_key(obf_dirname_table, dirname);
  2043. +   if (!obf_dirname) {
  2044. +       char buf[MAXPATHLEN];
  2045. +       obfuscate_fname(buf, MAXPATHLEN, dirname);
  2046. +       obf_dirname = strdup(buf);
  2047. +       insert_key_value(obf_dirname_table, dirname, obf_dirname, 0);
  2048. +   }
  2049. +   if (verbose > 2)
  2050. +       rprintf(FINFO, "obf_lookup_dirname(%s) = %s\n", dirname, obf_dirname);
  2051. +   return obf_dirname;
  2052. +}
  2053. +
  2054. +int obf_load()
  2055. +{
  2056. +   int fd;
  2057. +   STRUCT_STAT st;
  2058. +   int32 version;
  2059. +   char fname[MAXPATHLEN], fname_obf[MAXPATHLEN];
  2060. +
  2061. +   /*
  2062. +    * We need to avoid obfuscating "." and "..", so add them to the table.
  2063. +    */
  2064. +   char *dotdir = ".", *dotdotdir = "..";
  2065. +   insert_key_value(obf_table, dotdir, dotdir, 0);
  2066. +   insert_key_value(deobf_obf_table, dotdir, dotdir, 0);
  2067. +   insert_key_value(obf_table, dotdotdir, dotdotdir, 0);
  2068. +   insert_key_value(deobf_obf_table, dotdotdir, dotdotdir, 0);
  2069. +  
  2070. +   /*
  2071. +    * Also avoid obfuscating "*" and "**", since these are used in filters.
  2072. +    */
  2073. +   char *star = "*", *starstar = "**";
  2074. +   insert_key_value(obf_table, star, star, 0);
  2075. +   insert_key_value(deobf_obf_table, star, star, 0);
  2076. +   insert_key_value(obf_table, starstar, starstar, 0);
  2077. +   insert_key_value(deobf_obf_table, starstar, starstar, 0);
  2078. +
  2079. +   /*
  2080. +    * Also partial_dir which we use for storing partial transfers.
  2081. +    */
  2082. +   if (partial_dir)
  2083. +   {
  2084. +       char *partial = strdup(partial_dir);
  2085. +       insert_key_value(obf_table, partial, partial, 0);
  2086. +       insert_key_value(deobf_obf_table, partial, partial, 0);
  2087. +   }
  2088. +
  2089. +   fd = do_open(obfuscation_file, O_RDONLY, 0);
  2090. +   if (fd == -1) {
  2091. +       if (verbose > 0)
  2092. +       {
  2093. +           if (errno == ENOENT)
  2094. +               rprintf(FINFO, "obfuscation file %s: does not exist, a new file will be created\n", obfuscation_file);
  2095. +           else
  2096. +               rprintf(FINFO, "obfuscation file %s: could not open for loading, a new file will be created\n", obfuscation_file);
  2097. +       }
  2098. +       return 0;
  2099. +   }
  2100. +
  2101. +   if (do_fstat(fd, &st) != 0) {
  2102. +       if (verbose > 0)
  2103. +           rprintf(FINFO, "obfuscation file %s: could not fstat\n", obfuscation_file);
  2104. +       close(fd);
  2105. +       return 0;
  2106. +   }
  2107. +
  2108. +   if (st.st_size == 0) {
  2109. +       if (verbose > 0)
  2110. +           rprintf(FINFO, "obfuscation file %s: file is empty\n", obfuscation_file);
  2111. +       close(fd);
  2112. +       return 0;
  2113. +   }
  2114. +
  2115. +   if (verbose > 0)
  2116. +       rprintf(FINFO, "obfuscation file %s: loading\n", obfuscation_file);
  2117. +
  2118. +
  2119. +
  2120. +   version = read_int(fd);
  2121. +   if (version > OBF_VERSION) {
  2122. +       if (verbose > 0)
  2123. +           rprintf(FINFO, "obfuscation file %s: version %d > latest known version (%d), ignoring\n", obfuscation_file, version, OBF_VERSION);
  2124. +       return 0;
  2125. +   }
  2126. +
  2127. +   while (1) {
  2128. +       char *f, *o;
  2129. +       int age = 0;
  2130. +       read_vstring(fd, fname, MAXPATHLEN);
  2131. +       if (strlen(fname) == 0)
  2132. +           break; // Empty string signifies that we've reached the end of the file.
  2133. +
  2134. +       read_vstring(fd, fname_obf, MAXPATHLEN);
  2135. +
  2136. +       if (version >= 1)
  2137. +           age = read_int(fd);
  2138. +       f = strdup(fname);
  2139. +       o = strdup(fname_obf);
  2140. +       age++;
  2141. +       insert_key_value(obf_table, f, o, age);
  2142. +       insert_key_value(deobf_obf_table, o, f, age);
  2143. +   }
  2144. +   close(fd);
  2145. +   if (verbose > 0)
  2146. +       rprintf(FINFO, "obfuscation file %s: loaded\n", obfuscation_file);
  2147. +   return 1;
  2148. +}
  2149. +
  2150. +static void write_node(int fd, struct obf_node *p)
  2151. +{
  2152. +   if (p->age > OBF_MAX_AGE) {
  2153. +       if (verbose > 2)
  2154. +           rprintf(FINFO, "obf discarding key %s, value %s, age %d\n", p->key, p->value, p->age);
  2155. +       return;
  2156. +   }
  2157. +   if (verbose > 2)
  2158. +       rprintf(FINFO, "obf saving key %s, value %s, age %d\n", p->key, p->value, p->age);
  2159. +   write_vstring(fd, p->key, strlen(p->key));
  2160. +   write_vstring(fd, p->value, strlen(p->value));
  2161. +   write_int(fd, p->age);
  2162. +}
  2163. +
  2164. +void obf_save(void)
  2165. +{
  2166. +   int fd;
  2167. +   unsigned i;
  2168. +
  2169. +   char tmp[MAXPATHLEN];
  2170. +
  2171. +   if (!obfuscation_file || !obf_table_changed)
  2172. +       return;
  2173. +
  2174. +   get_tmpname(tmp, obfuscation_file);
  2175. +
  2176. +   fd = do_mkstemp(tmp, S_IRUSR|S_IWUSR);
  2177. +   if (fd == -1) {
  2178. +       if (verbose > 0)
  2179. +           rprintf(FINFO, "obfuscation file %s: could not open for saving\n", tmp);
  2180. +       return;
  2181. +   }
  2182. +   if (verbose > 2)
  2183. +       rprintf(FINFO, "obfuscation file %s: saving\n", tmp);
  2184. +
  2185. +   write_int(fd, (int32)OBF_VERSION);
  2186. +      
  2187. +   for (i = 0; i < OBF_TABLE_SIZE; ++i) {
  2188. +       struct obf_node *p;
  2189. +       for (p = obf_table[i]; p; p = p->next)
  2190. +           write_node(fd, p);
  2191. +   }
  2192. +
  2193. +   // Write an empty string to mark the end of file.
  2194. +   write_vstring(fd, "", 0);
  2195. +
  2196. +   close(fd);
  2197. +
  2198. +   if (verbose > 2)
  2199. +       rprintf(FINFO, "obfuscation file: renaming %s to %s\n", tmp, obfuscation_file);
  2200. +
  2201. +   if (robust_rename(tmp, obfuscation_file, NULL, S_IRUSR|S_IWUSR) < 0)
  2202. +       rprintf(FERROR, "obfuscation file: could not rename %s to %s\n", tmp, obfuscation_file);
  2203. +
  2204. +   return;
  2205. +}
  2206. Index: options.c
  2207. ===================================================================
  2208. --- options.c   (revision 2)
  2209. +++ options.c   (working copy)
  2210. @@ -104,6 +104,7 @@
  2211.  int safe_symlinks = 0;
  2212.  int copy_unsafe_links = 0;
  2213.  int size_only = 0;
  2214. +int times_only = 0;
  2215.  int daemon_bwlimit = 0;
  2216.  int bwlimit = 0;
  2217.  int fuzzy_basis = 0;
  2218. @@ -122,6 +123,8 @@
  2219.  int delay_updates = 0;
  2220.  long block_size = 0; /* "long" because popt can't set an int32. */
  2221.  char *skip_compress = NULL;
  2222. +int backup_nt_streams = 0;
  2223. +int restore_nt_streams = 0;
  2224.  
  2225.  /** Network address family. **/
  2226.  int default_af_hint
  2227. @@ -161,6 +164,12 @@
  2228.  char *logfile_format = NULL;
  2229.  char *stdout_format = NULL;
  2230.  char *password_file = NULL;
  2231. +char *source_filter = NULL;
  2232. +char *dest_filter = NULL;
  2233. +char *basis_filter = NULL;
  2234. +char *file_size_cache = NULL;
  2235. +char *obfuscation_file = NULL;
  2236. +char *source_filter_tmp = "/tmp/";
  2237.  char *rsync_path = RSYNC_PATH;
  2238.  char *backup_dir = NULL;
  2239.  char backup_dir_buf[MAXPATHLEN];
  2240. @@ -387,6 +396,7 @@
  2241.    rprintf(F,"     --contimeout=SECONDS    set daemon connection timeout in seconds\n");
  2242.    rprintf(F," -I, --ignore-times          don't skip files that match in size and mod-time\n");
  2243.    rprintf(F,"     --size-only             skip files that match in size\n");
  2244. +  rprintf(F,"     --times-only            skip files that match in mod-time\n");
  2245.    rprintf(F,"     --modify-window=NUM     compare mod-times with reduced accuracy\n");
  2246.    rprintf(F," -T, --temp-dir=DIR          create temporary files in directory DIR\n");
  2247.    rprintf(F," -y, --fuzzy                 find similar file for basis if no dest file\n");
  2248. @@ -426,6 +436,11 @@
  2249.    rprintf(F,"     --write-batch=FILE      write a batched update to FILE\n");
  2250.    rprintf(F,"     --only-write-batch=FILE like --write-batch but w/o updating destination\n");
  2251.    rprintf(F,"     --read-batch=FILE       read a batched update from FILE\n");
  2252. +  rprintf(F,"     --source-filter=COMMAND filter file through COMMAND at source\n");
  2253. +  rprintf(F,"     --dest-filter=COMMAND   filter file through COMMAND at destination\n");
  2254. +  rprintf(F,"     --basis-filter=COMMAND  filter basis file through COMMAND at destination\n");
  2255. +  rprintf(F,"     --file-size-cache=FILE  caches sizes of sent files in FILE\n");
  2256. +  rprintf(F,"     --source-filter-tmp=DIR use DIR instead of /tmp/ for temporary files created by --source-filter\n");
  2257.    rprintf(F,"     --protocol=NUM          force an older protocol version to be used\n");
  2258.  #ifdef ICONV_OPTION
  2259.    rprintf(F,"     --iconv=CONVERT_SPEC    request charset conversion of filenames\n");
  2260. @@ -529,6 +544,7 @@
  2261.    {"chmod",            0,  POPT_ARG_STRING, 0, OPT_CHMOD, 0, 0 },
  2262.    {"ignore-times",    'I', POPT_ARG_NONE,   &ignore_times, 0, 0, 0 },
  2263.    {"size-only",        0,  POPT_ARG_NONE,   &size_only, 0, 0, 0 },
  2264. +  {"times-only",       0,  POPT_ARG_NONE,   &times_only , 0, 0, 0 },
  2265.    {"one-file-system", 'x', POPT_ARG_NONE,   0, 'x', 0, 0 },
  2266.    {"no-one-file-system",0, POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
  2267.    {"no-x",             0,  POPT_ARG_VAL,    &one_file_system, 0, 0, 0 },
  2268. @@ -644,6 +660,12 @@
  2269.    {"password-file",    0,  POPT_ARG_STRING, &password_file, 0, 0, 0 },
  2270.    {"blocking-io",      0,  POPT_ARG_VAL,    &blocking_io, 1, 0, 0 },
  2271.    {"no-blocking-io",   0,  POPT_ARG_VAL,    &blocking_io, 0, 0, 0 },
  2272. +  {"source-filter",    0,  POPT_ARG_STRING, &source_filter, 0, 0, 0 },
  2273. +  {"dest-filter",      0,  POPT_ARG_STRING, &dest_filter, 0, 0, 0 },
  2274. +  {"basis-filter",     0,  POPT_ARG_STRING, &basis_filter, 0, 0, 0 },
  2275. +  {"file-size-cache",  0,  POPT_ARG_STRING, &file_size_cache, 0, 0, 0 },
  2276. +  {"obfuscation-file", 0,  POPT_ARG_STRING, &obfuscation_file, 0, 0, 0 },
  2277. +  {"source-filter-tmp",0,  POPT_ARG_STRING, &source_filter_tmp, 0, 0, 0 },
  2278.    {"protocol",         0,  POPT_ARG_INT,    &protocol_version, 0, 0, 0 },
  2279.    {"checksum-seed",    0,  POPT_ARG_INT,    &checksum_seed, 0, 0, 0 },
  2280.    {"server",           0,  POPT_ARG_NONE,   0, OPT_SERVER, 0, 0 },
  2281. @@ -653,6 +675,8 @@
  2282.    {"daemon",           0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
  2283.    {"detach",           0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
  2284.    {"no-detach",        0,  POPT_ARG_NONE,   0, OPT_DAEMON, 0, 0 },
  2285. +  {"backup-nt-streams",0,  POPT_ARG_VAL,    &backup_nt_streams, 1, 0, 0 },
  2286. +  {"restore-nt-streams",0, POPT_ARG_VAL,    &restore_nt_streams, 1, 0, 0 },
  2287.    {0,0,0,0, 0, 0, 0}
  2288.  };
  2289.  
  2290. @@ -1972,14 +1996,53 @@
  2291.             args[ac++] = "--super";
  2292.         if (size_only)
  2293.             args[ac++] = "--size-only";
  2294. +       if (restore_nt_streams)
  2295. +           args[ac++] = "--restore-nt-streams";
  2296.     } else {
  2297.         if (skip_compress) {
  2298.             if (asprintf(&arg, "--skip-compress=%s", skip_compress) < 0)
  2299.                 goto oom;
  2300.             args[ac++] = arg;
  2301.         }
  2302. +       if (backup_nt_streams)
  2303. +           args[ac++] = "--backup-nt-streams";
  2304.     }
  2305.  
  2306. +   if (times_only && am_sender)
  2307. +       args[ac++] = "--times-only";
  2308. +
  2309. +   if (source_filter && !am_sender) {
  2310. +       /* Need to single quote the arg to keep the remote shell
  2311. +        * from splitting it.  FIXME: breaks if command has single quotes. */
  2312. +           if (asprintf(&arg, "--source-filter='%s'", source_filter) < 0)
  2313. +           goto oom;
  2314. +       args[ac++] = arg;
  2315. +   }
  2316. +
  2317. +   if (dest_filter && am_sender) {
  2318. +       /* Need to single quote the arg to keep the remote shell
  2319. +        * from splitting it.  FIXME: breaks if command has single quotes. */
  2320. +           if (asprintf(&arg, "--dest-filter='%s'", dest_filter) < 0)
  2321. +           goto oom;
  2322. +       args[ac++] = arg;
  2323. +   }
  2324. +
  2325. +   if (basis_filter && am_sender) {
  2326. +       /* Need to single quote the arg to keep the remote shell
  2327. +        * from splitting it.  FIXME: breaks if command has single quotes. */
  2328. +           if (asprintf(&arg, "--basis-filter='%s'", basis_filter) < 0)
  2329. +           goto oom;
  2330. +       args[ac++] = arg;
  2331. +   }
  2332. +
  2333. +   if (source_filter_tmp && source_filter && !am_sender) {
  2334. +       /* Need to single quote the arg to keep the remote shell
  2335. +        * from splitting it.  FIXME: breaks if command has single quotes. */
  2336. +           if (asprintf(&arg, "--source-filter-tmp='%s'", source_filter_tmp) < 0)
  2337. +           goto oom;
  2338. +       args[ac++] = arg;
  2339. +   }
  2340. +
  2341.     if (modify_window_set) {
  2342.         if (asprintf(&arg, "--modify-window=%d", modify_window) < 0)
  2343.             goto oom;
  2344. Index: pipe.c
  2345. ===================================================================
  2346. --- pipe.c  (revision 2)
  2347. +++ pipe.c  (working copy)
  2348. @@ -167,3 +167,77 @@
  2349.  
  2350.     return pid;
  2351.  }
  2352. +
  2353. +pid_t run_filter(char *command[], int out, int *pipe_to_filter)
  2354. +{
  2355. +   pid_t pid;
  2356. +   int pipefds[2];
  2357. +
  2358. +   if (verbose >= 2)
  2359. +       print_child_argv("opening connection using:", command);
  2360. +
  2361. +   if (pipe(pipefds) < 0) {
  2362. +       rsyserr(FERROR, errno, "pipe");
  2363. +       exit_cleanup(RERR_IPC);
  2364. +   }
  2365. +
  2366. +   pid = fork();
  2367. +   if (pid == -1) {
  2368. +       rsyserr(FERROR, errno, "fork");
  2369. +       exit_cleanup(RERR_IPC);
  2370. +   }
  2371. +
  2372. +   if (pid == 0) {
  2373. +       if (dup2(pipefds[0], STDIN_FILENO) < 0
  2374. +        || close(pipefds[1]) < 0
  2375. +        || dup2(out, STDOUT_FILENO) < 0) {
  2376. +           rsyserr(FERROR, errno, "Failed dup/close");
  2377. +           exit_cleanup(RERR_IPC);
  2378. +       }
  2379. +       umask(orig_umask);
  2380. +       set_blocking(STDIN_FILENO);
  2381. +       if (blocking_io)
  2382. +           set_blocking(STDOUT_FILENO);
  2383. +       execvp(command[0], command);
  2384. +       rsyserr(FERROR, errno, "Failed to exec %s", command[0]);
  2385. +       exit_cleanup(RERR_IPC);
  2386. +   }
  2387. +
  2388. +   if (close(pipefds[0]) < 0) {
  2389. +       rsyserr(FERROR, errno, "Failed to close");
  2390. +       exit_cleanup(RERR_IPC);
  2391. +   }
  2392. +
  2393. +   *pipe_to_filter = pipefds[1];
  2394. +
  2395. +   return pid;
  2396. +}
  2397. +
  2398. +pid_t run_filter_on_file(char *command[], int out, int in)
  2399. +{
  2400. +   pid_t pid;
  2401. +
  2402. +   if (verbose >= 2)
  2403. +       print_child_argv("opening connection using:", command);
  2404. +
  2405. +   pid = fork();
  2406. +   if (pid == -1) {
  2407. +       rsyserr(FERROR, errno, "fork");
  2408. +       exit_cleanup(RERR_IPC);
  2409. +   }
  2410. +
  2411. +   if (pid == 0) {
  2412. +       if ((in >= 0 && dup2(in, STDIN_FILENO) < 0)
  2413. +        || (out >= 0 && dup2(out, STDOUT_FILENO)) < 0) {
  2414. +           rsyserr(FERROR, errno, "Failed to dup2");
  2415. +           exit_cleanup(RERR_IPC);
  2416. +       }
  2417. +       if (blocking_io)
  2418. +           set_blocking(STDOUT_FILENO);
  2419. +       execvp(command[0], command);
  2420. +       rsyserr(FERROR, errno, "Failed to exec %s", command[0]);
  2421. +       exit_cleanup(RERR_IPC);
  2422. +   }
  2423. +
  2424. +   return pid;
  2425. +}
  2426. Index: proto.h
  2427. ===================================================================
  2428. --- proto.h (revision 2)
  2429. +++ proto.h (working copy)
  2430. @@ -85,7 +85,7 @@
  2431.  int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks);
  2432.  int change_pathname(struct file_struct *file, const char *dir, int dirlen);
  2433.  struct file_struct *make_file(const char *fname, struct file_list *flist,
  2434. -                 STRUCT_STAT *stp, int flags, int filter_level);
  2435. +                 STRUCT_STAT *stp, int flags, int filter_level, const char *real_fname);
  2436.  void unmake_file(struct file_struct *file);
  2437.  void send_extra_file_list(int f, int at_least);
  2438.  struct file_list *send_file_list(int f, int argc, char *argv[]);
  2439. @@ -100,7 +100,14 @@
  2440.  int f_name_has_prefix(const struct file_struct *f1, const struct file_struct *f2);
  2441.  char *f_name_buf(void);
  2442.  char *f_name(const struct file_struct *f, char *fbuf);
  2443. +char *f_name_obf(const struct file_struct *f, char *fbuf);
  2444. +char *f_name_maybe_obf(const struct file_struct *f, char *fbuf);
  2445.  struct file_list *get_dirlist(char *dirname, int dlen, int flags);
  2446. +void fscache_clear(void);
  2447. +int fscache_lookup(const char *fname, int64 *pSize);
  2448. +void fscache_insert(const char *fname, int64 size);
  2449. +int fscache_load();
  2450. +void fscache_save(void);
  2451.  int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp);
  2452.  void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret,
  2453.          stat_x *sxp, int32 iflags, uchar fnamecmp_type,
  2454. @@ -241,6 +248,7 @@
  2455.  void log_delete(const char *fname, int mode);
  2456.  void log_exit(int code, const char *file, int line);
  2457.  pid_t wait_process(pid_t pid, int *status_ptr, int flags);
  2458. +void wait_process_with_flush(pid_t pid, int *exit_code_ptr);
  2459.  int child_main(int argc, char *argv[]);
  2460.  void start_server(int f_in, int f_out, int argc, char *argv[]);
  2461.  int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[]);
  2462. @@ -249,6 +257,26 @@
  2463.  int main(int argc,char *argv[]);
  2464.  void match_sums(int f, struct sum_struct *s, struct map_struct *buf, OFF_T len);
  2465.  void match_report(void);
  2466. +int ntfs_streams_to_fd(const char *path, int fd);
  2467. +int fd_to_ntfs_streams(int fd, const char *path);
  2468. +void ntstreams_dirname(char *dest, size_t destsize, const char *dirname);
  2469. +size_t ntstreams_filename(char *dest, char *orig, size_t destsize, const char *dirname, const char *basename);
  2470. +int ntstreams_orig_filename(char *dest, size_t destsize, const char *dirname, const char *basename);
  2471. +char *ntstreams_get_last_error_message();
  2472. +const char *ntstreams_get_last_error_what();
  2473. +int set_privilege_backup(int bEnablePrivilege);
  2474. +int set_privilege_restore(int bEnablePrivilege);
  2475. +int set_privilege_security(int bEnablePrivilege);
  2476. +int set_privilege_take_ownership(int bEnablePrivilege);
  2477. +int set_privilege(const char *lpszPrivilege, int bEnablePrivilege);
  2478. +void ntstreams_restore_update(const char *fname, const struct file_struct *file);
  2479. +void deobfuscate_fname(char *fname, int size, const char *fname_obf);
  2480. +void obfuscate_pattern(char *pat_obf, int size, const char *pat);
  2481. +void obfuscate_argpath(char *argpath_obf, int size, const char *argpath);
  2482. +const char *obf_lookup_basename(const char *component);
  2483. +const char *obf_lookup_dirname(const char *dirname);
  2484. +int obf_load();
  2485. +void obf_save(void);
  2486.  void usage(enum logcode F);
  2487.  void option_error(void);
  2488.  int parse_arguments(int *argc_p, const char ***argv_p);
  2489. @@ -260,6 +288,8 @@
  2490.  pid_t piped_child(char **command, int *f_in, int *f_out);
  2491.  pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
  2492.           int (*child_main)(int, char*[]));
  2493. +pid_t run_filter(char *command[], int out, int *pipe_to_filter);
  2494. +pid_t run_filter_on_file(char *command[], int out, int in);
  2495.  void set_current_file_index(struct file_struct *file, int ndx);
  2496.  void end_progress(OFF_T size);
  2497.  void show_progress(OFF_T ofs, OFF_T size);
  2498. @@ -382,6 +412,16 @@
  2499.  int flist_ndx_pop(flist_ndx_list *lp);
  2500.  void *expand_item_list(item_list *lp, size_t item_size,
  2501.                const char *desc, int incr);
  2502. +char *my_strtok(char *s);
  2503. +void tokenize_filter_args(
  2504. +       /* OUT     */ char *filter_argv[],
  2505. +       /* IN/OUT  */ char *filter,
  2506. +       /* IN      */ const char *filter_type);
  2507. +enum filter_args_info substitute_filter_args(
  2508. +       /* OUT */ char *filter_argv_subst[],
  2509. +       /* IN  */ char *source_fname,
  2510. +       /* IN  */ char *dest_fname,
  2511. +       /* IN  */ char *filter_argv[]);
  2512.  void free_xattr(stat_x *sxp);
  2513.  int get_xattr(const char *fname, stat_x *sxp);
  2514.  int copy_xattrs(const char *source, const char *dest);
  2515. Index: receiver.c
  2516. ===================================================================
  2517. --- receiver.c  (revision 2)
  2518. +++ receiver.c  (working copy)
  2519. @@ -53,10 +53,12 @@
  2520.  extern mode_t orig_umask;
  2521.  extern struct stats stats;
  2522.  extern char *tmpdir;
  2523. +extern char *dest_filter;
  2524.  extern char *partial_dir;
  2525.  extern char *basis_dir[MAX_BASIS_DIRS+1];
  2526.  extern struct file_list *cur_flist, *first_flist, *dir_flist;
  2527.  extern struct filter_list_struct daemon_filter_list;
  2528. +extern int restore_nt_streams;
  2529.  
  2530.  static struct bitbag *delayed_bits = NULL;
  2531.  static int phase = 0, redoing = 0;
  2532. @@ -442,12 +444,13 @@
  2533.   * Receiver process runs on the same host as the generator process. */
  2534.  int recv_files(int f_in, char *local_name)
  2535.  {
  2536. -   int fd1,fd2;
  2537. +   int fd1,fd2,fd2_dest_filter = -1;
  2538.     STRUCT_STAT st;
  2539.     int iflags, xlen;
  2540.     char *fname, fbuf[MAXPATHLEN];
  2541.     char xname[MAXPATHLEN];
  2542.     char fnametmp[MAXPATHLEN];
  2543. +   char fnametmp_dest_filter[MAXPATHLEN];
  2544.     char *fnamecmp, *partialptr;
  2545.     char fnamecmpbuf[MAXPATHLEN];
  2546.     uchar fnamecmp_type;
  2547. @@ -461,6 +464,10 @@
  2548.     const char *parent_dirname = "";
  2549.  #endif
  2550.     int ndx, recv_ok;
  2551. +   pid_t pid = 0;
  2552. +   char *filter_argv[MAX_FILTER_ARGS + 1];
  2553. +   char *filter_argv_subst[MAX_FILTER_ARGS + 1];
  2554. +   enum filter_args_info fai = fai_has_none;
  2555.  
  2556.     if (verbose > 2)
  2557.         rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);
  2558. @@ -468,7 +475,25 @@
  2559.     if (delay_updates)
  2560.         delayed_bits = bitbag_create(cur_flist->used + 1);
  2561.  
  2562. +   if (dest_filter)
  2563. +       tokenize_filter_args(filter_argv, dest_filter, "dest");
  2564. +
  2565. +   if (restore_nt_streams) {
  2566. +       if (verbose >2)
  2567. +           rprintf(FINFO, "requesting SeRestorePrivilege\n");
  2568. +       if (!set_privilege_restore(1))
  2569. +           rprintf(FERROR, "could not obtain SeRestorePrivilege: %s\n",
  2570. +               ntstreams_get_last_error_message());
  2571. +       if (verbose >2)
  2572. +           rprintf(FINFO, "requesting SeSecurityPrivilege\n");
  2573. +       if (!set_privilege_security(1))
  2574. +           rprintf(FERROR, "could not obtain SeSecurityPrivilege: %s\n",
  2575. +               ntstreams_get_last_error_message());
  2576. +   }
  2577. +
  2578.     while (1) {
  2579. +       int unlink_basis_filter_tmp = 0;
  2580. +       fai = fai_has_none;
  2581.         cleanup_disable();
  2582.  
  2583.         /* This call also sets cur_flist. */
  2584. @@ -502,6 +527,9 @@
  2585.         if (verbose > 2)
  2586.             rprintf(FINFO, "recv_files(%s)\n", fname);
  2587.  
  2588. +       if (restore_nt_streams)
  2589. +           ntstreams_restore_update(fname, file);
  2590. +
  2591.  #ifdef SUPPORT_XATTRS
  2592.         if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && do_xfers)
  2593.             recv_xattr_request(file, f_in);
  2594. @@ -599,6 +627,10 @@
  2595.             case FNAMECMP_BACKUP:
  2596.                 fnamecmp = get_backup_name(fname);
  2597.                 break;
  2598. +           case FNAMECMP_FILTERED:
  2599. +               unlink_basis_filter_tmp = 1;
  2600. +               fnamecmp = xname;
  2601. +               break;
  2602.             case FNAMECMP_FUZZY:
  2603.                 if (file->dirname) {
  2604.                     pathjoin(fnamecmpbuf, MAXPATHLEN,
  2605. @@ -713,6 +745,7 @@
  2606.         /* We now check to see if we are writing the file "inplace" */
  2607.         if (inplace)  {
  2608.             fd2 = do_open(fname, O_WRONLY|O_CREAT, 0600);
  2609. +           strncpy(fnametmp, fname, MAXPATHLEN);
  2610.             if (fd2 == -1) {
  2611.                 rsyserr(FERROR_XFER, errno, "open %s failed",
  2612.                     full_fname(fname));
  2613. @@ -738,6 +771,33 @@
  2614.         else if (!am_server && verbose && do_progress)
  2615.             rprintf(FINFO, "%s\n", fname);
  2616.  
  2617. +       if (dest_filter) {
  2618. +           int tmpfd = open_tmpfile(fnametmp_dest_filter, fname, file);
  2619. +           fai = substitute_filter_args(filter_argv_subst, fnametmp_dest_filter, fnametmp, filter_argv);
  2620. +           if (fai == fai_has_none) {
  2621. +               pid = run_filter(filter_argv, fd2, &fd2);
  2622. +               if (tmpfd != -1)
  2623. +               {
  2624. +                   close(tmpfd);
  2625. +                   unlink(fnametmp_dest_filter);
  2626. +               }
  2627. +               fd2_dest_filter = -1;
  2628. +           } else {
  2629. +               if (tmpfd == -1) {
  2630. +                   discard_receive_data(f_in, F_LENGTH(file));
  2631. +                   if (fd1 != -1)
  2632. +                       close(fd1);
  2633. +                   if (fd2 != -1)
  2634. +                       close(fd2);
  2635. +                   if (inc_recurse)
  2636. +                       send_msg_int(MSG_NO_SEND, ndx);
  2637. +                   continue;
  2638. +               }
  2639. +               fd2_dest_filter = fd2;
  2640. +               fd2 = tmpfd;
  2641. +           }
  2642. +       }
  2643. +
  2644.         /* recv file data */
  2645.         recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size,
  2646.                        fname, fd2, F_LENGTH(file));
  2647. @@ -746,12 +806,46 @@
  2648.  
  2649.         if (fd1 != -1)
  2650.             close(fd1);
  2651. -       if (close(fd2) < 0) {
  2652. +
  2653. +       if (fd2 >= 0 && close(fd2) < 0) {
  2654.             rsyserr(FERROR, errno, "close failed on %s",
  2655.                 full_fname(fnametmp));
  2656.             exit_cleanup(RERR_FILEIO);
  2657.         }
  2658.  
  2659. +       if (dest_filter && fai != fai_has_none) {
  2660. +           int status;
  2661. +           if (fai & fai_has_source) {
  2662. +               fd2 = -1;
  2663. +           } else {
  2664. +               fd2 = do_open(fnametmp_dest_filter, O_RDONLY, 0);
  2665. +           }
  2666. +           if (fai & fai_has_dest) {
  2667. +               close(fd2_dest_filter);
  2668. +               fd2_dest_filter = -1;
  2669. +           }
  2670. +           pid = run_filter_on_file(filter_argv_subst, fd2_dest_filter, fd2);
  2671. +           if (fd2 >= 0) close(fd2);
  2672. +           if (fd2_dest_filter >= 0) close(fd2_dest_filter);
  2673. +           wait_process_with_flush(pid, &status);
  2674. +           if (status != 0) {
  2675. +               rprintf(FERROR, "dest-filter %s exited code: %d\n",
  2676. +                   dest_filter, status);
  2677. +               continue;
  2678. +           }
  2679. +           unlink(fnametmp_dest_filter);
  2680. +       }
  2681. +
  2682. +       if (dest_filter && fai == fai_has_none) {
  2683. +           int status;
  2684. +           wait_process_with_flush(pid, &status);
  2685. +           if (status != 0) {
  2686. +               rprintf(FERROR, "filter %s exited code: %d\n",
  2687. +                   dest_filter, status);
  2688. +               continue;
  2689. +           }
  2690. +       }
  2691. +
  2692.         if ((recv_ok && (!delay_updates || !partialptr)) || inplace) {
  2693.             if (partialptr == fname)
  2694.                 partialptr = NULL;
  2695. @@ -781,6 +875,9 @@
  2696.         } else
  2697.             do_unlink(fnametmp);
  2698.  
  2699. +       if (unlink_basis_filter_tmp)
  2700. +           do_unlink(fnamecmp);
  2701. +
  2702.         cleanup_disable();
  2703.  
  2704.         if (read_batch)
  2705. @@ -832,6 +929,9 @@
  2706.             break;
  2707.         }
  2708.     }
  2709. +   if (restore_nt_streams)
  2710. +       ntstreams_restore_update(NULL, NULL);
  2711. +
  2712.     if (make_backups < 0)
  2713.         make_backups = -make_backups;
  2714.  
  2715. Index: rsync.1
  2716. ===================================================================
  2717. --- rsync.1 (revision 2)
  2718. +++ rsync.1 (working copy)
  2719. @@ -461,6 +461,7 @@
  2720.       \-\-contimeout=SECONDS    set daemon connection timeout in seconds
  2721.   \-I, \-\-ignore\-times          don'\&t skip files that match size and time
  2722.       \-\-size\-only             skip files that match in size
  2723. +     \-\-times\-only            skip files that match in mod-time
  2724.       \-\-modify\-window=NUM     compare mod\-times with reduced accuracy
  2725.   \-T, \-\-temp\-dir=DIR          create temporary files in directory DIR
  2726.   \-y, \-\-fuzzy                 find similar file for basis if no dest file
  2727. @@ -500,6 +501,8 @@
  2728.       \-\-write\-batch=FILE      write a batched update to FILE
  2729.       \-\-only\-write\-batch=FILE like \-\-write\-batch but w/o updating dest
  2730.       \-\-read\-batch=FILE       read a batched update from FILE
  2731. +     \-\-source\-filter=COMMAND filter file through COMMAND at source
  2732. +     \-\-dest\-filter=COMMAND   filter file through COMMAND at destination
  2733.       \-\-protocol=NUM          force an older protocol version to be used
  2734.       \-\-iconv=CONVERT_SPEC    request charset conversion of filenames
  2735.       \-\-checksum\-seed=NUM     set block/file checksum seed (advanced)
  2736. @@ -2467,6 +2470,35 @@
  2737.  If \fIFILE\fP is \fB\-\fP, the batch data will be read from standard input.
  2738.  See the \(dq\&BATCH MODE\(dq\& section for details.
  2739.  .IP
  2740. +.IP "\fB\-\-source\-filter=COMMAND\fP"
  2741. +This option allows the user to specify a
  2742. +filter program that will be applied to the contents of all transferred
  2743. +regular files before the data is sent to destination.  COMMAND will receive
  2744. +the data on its standard input and it should write the filtered data to
  2745. +standard output.  COMMAND should exit non-zero if it cannot process the
  2746. +data or if it encounters an error when writing the data to stdout.
  2747. +.IP
  2748. +Example: \-\-source\-filter=\(dq\&gzip \-9\(dq\& will cause remote files to be
  2749. +compressed.
  2750. +Use of \-\-source\-filter automatically enables \-\-whole\-file.
  2751. +If your filter does not output the same number of bytes that it received on
  2752. +input, you should use \-\-times\-only to disable size and content checks on
  2753. +subsequent rsync runs.
  2754. +.IP
  2755. +.IP "\fB\-\-dest\-filter=COMMAND\fP"
  2756. +This option allows you to specify a filter
  2757. +program that will be applied to the contents of all transferred regular
  2758. +files before the data is written to disk.  COMMAND will receive the data on
  2759. +its standard input and it should write the filtered data to standard
  2760. +output.  COMMAND should exit non-zero if it cannot process the data or if
  2761. +it encounters an error when writing the data to stdout.
  2762. +.IP
  2763. +Example: \-\-dest\-filter=\(dq\&gzip \-9\(dq\& will cause remote files to be compressed.
  2764. +Use of \-\-dest\-filter automatically enables \-\-whole\-file.
  2765. +If your filter does not output the same number of bytes that it
  2766. +received on input, you should use \-\-times\-only to disable size and
  2767. +content checks on subsequent rsync runs.
  2768. +.IP
  2769.  .IP "\fB\-\-protocol=NUM\fP"
  2770.  Force an older protocol version to be used.  This
  2771.  is useful for creating a batch file that is compatible with an older
  2772. Index: rsync.h
  2773. ===================================================================
  2774. --- rsync.h (revision 2)
  2775. +++ rsync.h (working copy)
  2776. @@ -82,6 +82,10 @@
  2777.  #define FLAG_SKIP_GROUP (1<<10)    /* receiver/generator */
  2778.  #define FLAG_TIME_FAILED (1<<11)/* generator */
  2779.  
  2780. +/* NT Streams */
  2781. +#define FLAG_NT_STREAM (1<<12) /* sender */
  2782. +#define FLAG_NT_STREAM_DIR (1<<13) /* sender */
  2783. +
  2784.  /* These flags are passed to functions but not stored. */
  2785.  
  2786.  #define FLAG_DIVERT_DIRS (1<<16)   /* sender, but must be unique */
  2787. @@ -143,6 +147,7 @@
  2788.  #define IOERR_DEL_LIMIT (1<<2)
  2789.  
  2790.  #define MAX_ARGS 1000
  2791. +#define MAX_FILTER_ARGS 100
  2792.  #define MAX_BASIS_DIRS 20
  2793.  #define MAX_SERVER_ARGS (MAX_BASIS_DIRS*2 + 100)
  2794.  
  2795. @@ -174,6 +179,7 @@
  2796.  #define FNAMECMP_PARTIAL_DIR   0x81
  2797.  #define FNAMECMP_BACKUP    0x82
  2798.  #define FNAMECMP_FUZZY     0x83
  2799. +#define FNAMECMP_FILTERED  0x84
  2800.  
  2801.  /* For use by the itemize_changes code */
  2802.  #define ITEM_REPORT_ATIME (1<<0)
  2803. @@ -645,6 +651,8 @@
  2804.     uint32 len32;       /* Lowest 32 bits of the file's length */
  2805.     uint16 mode;        /* The item's type and permissions */
  2806.     uint16 flags;       /* The FLAG_* bits for this item */
  2807. +   const char *dirname_obf; /* Obfuscated dir name */
  2808. +   const char *basename_obf; /* Obfuscated basename */
  2809.     const char basename[1]; /* The basename (AKA filename) follows */
  2810.  };
  2811.  
  2812. @@ -932,6 +940,8 @@
  2813.  #define ACL_READY(sx) ((sx).acc_acl != NULL)
  2814.  #define XATTR_READY(sx) ((sx).xattr != NULL)
  2815.  
  2816. +enum filter_args_info { fai_has_none = 0, fai_has_source = 1, fai_has_dest = 2 };
  2817. +
  2818.  #include "proto.h"
  2819.  
  2820.  #ifndef SUPPORT_XATTRS
  2821. @@ -1168,3 +1178,5 @@
  2822.  #ifdef MAINTAINER_MODE
  2823.  const char *get_panic_action(void);
  2824.  #endif
  2825. +
  2826. +#define OBF_ARGPATH_START_MARKER "!!!"
  2827. Index: rsync.yo
  2828. ===================================================================
  2829. --- rsync.yo    (revision 2)
  2830. +++ rsync.yo    (working copy)
  2831. @@ -386,6 +386,7 @@
  2832.       --contimeout=SECONDS    set daemon connection timeout in seconds
  2833.   -I, --ignore-times          don't skip files that match size and time
  2834.       --size-only             skip files that match in size
  2835. +     --times-only            skip files that match in mod-time
  2836.       --modify-window=NUM     compare mod-times with reduced accuracy
  2837.   -T, --temp-dir=DIR          create temporary files in directory DIR
  2838.   -y, --fuzzy                 find similar file for basis if no dest file
  2839. @@ -425,6 +426,8 @@
  2840.       --write-batch=FILE      write a batched update to FILE
  2841.       --only-write-batch=FILE like --write-batch but w/o updating dest
  2842.       --read-batch=FILE       read a batched update from FILE
  2843. +     --source-filter=COMMAND filter file through COMMAND at source
  2844. +     --dest-filter=COMMAND   filter file through COMMAND at destination
  2845.       --protocol=NUM          force an older protocol version to be used
  2846.       --iconv=CONVERT_SPEC    request charset conversion of filenames
  2847.       --checksum-seed=NUM     set block/file checksum seed (advanced)
  2848. @@ -2150,6 +2153,33 @@
  2849.  If em(FILE) is bf(-), the batch data will be read from standard input.
  2850.  See the "BATCH MODE" section for details.
  2851.  
  2852. +dit(bf(--source-filter=COMMAND)) This option allows the user to specify a
  2853. +filter program that will be applied to the contents of all transferred
  2854. +regular files before the data is sent to destination.  COMMAND will receive
  2855. +the data on its standard input and it should write the filtered data to
  2856. +standard output.  COMMAND should exit non-zero if it cannot process the
  2857. +data or if it encounters an error when writing the data to stdout.
  2858. +
  2859. +Example: --source-filter="gzip -9" will cause remote files to be
  2860. +compressed.
  2861. +Use of --source-filter automatically enables --whole-file.
  2862. +If your filter does not output the same number of bytes that it received on
  2863. +input, you should use --times-only to disable size and content checks on
  2864. +subsequent rsync runs.
  2865. +
  2866. +dit(bf(--dest-filter=COMMAND)) This option allows you to specify a filter
  2867. +program that will be applied to the contents of all transferred regular
  2868. +files before the data is written to disk.  COMMAND will receive the data on
  2869. +its standard input and it should write the filtered data to standard
  2870. +output.  COMMAND should exit non-zero if it cannot process the data or if
  2871. +it encounters an error when writing the data to stdout.
  2872. +
  2873. +Example: --dest-filter="gzip -9" will cause remote files to be compressed.
  2874. +Use of --dest-filter automatically enables --whole-file.
  2875. +If your filter does not output the same number of bytes that it
  2876. +received on input, you should use --times-only to disable size and
  2877. +content checks on subsequent rsync runs.
  2878. +
  2879.  dit(bf(--protocol=NUM)) Force an older protocol version to be used.  This
  2880.  is useful for creating a batch file that is compatible with an older
  2881.  version of rsync.  For instance, if rsync 2.6.4 is being used with the
  2882. Index: sender.c
  2883. ===================================================================
  2884. --- sender.c    (revision 2)
  2885. +++ sender.c    (working copy)
  2886. @@ -42,8 +42,12 @@
  2887.  extern int inplace;
  2888.  extern int batch_fd;
  2889.  extern int write_batch;
  2890. +extern char *source_filter;
  2891. +extern char *source_filter_tmp;
  2892. +extern char *file_size_cache;
  2893.  extern struct stats stats;
  2894.  extern struct file_list *cur_flist, *first_flist, *dir_flist;
  2895. +extern int backup_nt_streams;
  2896.  
  2897.  /**
  2898.   * @file
  2899. @@ -163,7 +167,8 @@
  2900.     struct sum_struct *s;
  2901.     struct map_struct *mbuf = NULL;
  2902.     STRUCT_STAT st;
  2903. -   char fname[MAXPATHLEN], xname[MAXPATHLEN];
  2904. +   char fname[MAXPATHLEN], xname[MAXPATHLEN], ntstreams_tmpname[MAXPATHLEN];
  2905. +   char *fname_to_open;
  2906.     const char *path, *slash;
  2907.     uchar fnamecmp_type;
  2908.     int iflags, xlen;
  2909. @@ -175,10 +180,29 @@
  2910.     int f_xfer = write_batch < 0 ? batch_fd : f_out;
  2911.     int save_io_error = io_error;
  2912.     int ndx, j;
  2913. +   char *filter_argv[MAX_FILTER_ARGS + 1];
  2914. +   char tmp[MAXPATHLEN];
  2915. +   int unlink_tmp = 0, unlink_ntstreams_tmp = 0;
  2916.  
  2917. +   if (source_filter)
  2918. +       tokenize_filter_args(filter_argv, source_filter, "source");
  2919. +
  2920.     if (verbose > 2)
  2921.         rprintf(FINFO, "send_files starting\n");
  2922.  
  2923. +   if (backup_nt_streams) {
  2924. +       if (verbose >2)
  2925. +           rprintf(FINFO, "requesting SeBackupPrivilege\n");
  2926. +       if (!set_privilege_backup(1))
  2927. +           rprintf(FERROR, "could not obtain SeBackupPrivilege: %s\n",
  2928. +               ntstreams_get_last_error_message());
  2929. +       if (verbose >2)
  2930. +           rprintf(FINFO, "requesting SeSecurityPrivilege\n");
  2931. +       if (!set_privilege_security(1))
  2932. +           rprintf(FERROR, "could not obtain SeSecurityPrivilege: %s\n",
  2933. +               ntstreams_get_last_error_message());
  2934. +   }
  2935. +
  2936.     while (1) {
  2937.         if (inc_recurse)
  2938.             send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
  2939. @@ -218,6 +242,7 @@
  2940.         if (!change_pathname(file, NULL, 0))
  2941.             continue;
  2942.         f_name(file, fname);
  2943. +       fname_to_open = fname;
  2944.  
  2945.         if (verbose > 2)
  2946.             rprintf(FINFO, "send_files(%d, %s%s%s)\n", ndx, path,slash,fname);
  2947. @@ -279,14 +304,41 @@
  2948.             exit_cleanup(RERR_PROTOCOL);
  2949.         }
  2950.  
  2951. -       fd = do_open(fname, O_RDONLY, 0);
  2952. +       unlink_ntstreams_tmp = 0;
  2953. +       if (backup_nt_streams && file->flags & FLAG_NT_STREAM) {
  2954. +           int fd2;
  2955. +           char orig_fname[MAXPATHLEN];
  2956. +           ntstreams_orig_filename(orig_fname, MAXPATHLEN, file->dirname, file->basename);
  2957. +           if (verbose > 2) {
  2958. +               rprintf(FINFO, "reading nt streams for %s\n", orig_fname);
  2959. +           }
  2960. +           pathjoin(ntstreams_tmpname, MAXPATHLEN, source_filter_tmp, "rsync-nt_streamsXXXXXX");
  2961. +           fd2 = mkstemp(ntstreams_tmpname);
  2962. +           if (fd2 == -1) {
  2963. +               rprintf(FERROR, "mkstemp %s failed, skipping nt streams: %s\n",
  2964. +                   orig_fname, strerror(errno));
  2965. +               continue;
  2966. +           }
  2967. +           if (ntfs_streams_to_fd(orig_fname, fd2) <= 0) {
  2968. +               rprintf(FERROR, "ntfs_streams_to_fd %s failed on %s, skipping nt streams: %s\n",
  2969. +                   ntstreams_get_last_error_what(), orig_fname, ntstreams_get_last_error_message());
  2970. +               close(fd2);
  2971. +               continue;
  2972. +           }
  2973. +           close(fd2);
  2974. +           unlink_ntstreams_tmp = 1;
  2975. +           fname_to_open = ntstreams_tmpname;
  2976. +       }
  2977. +
  2978. +       unlink_tmp = 0;
  2979. +       fd = do_open(fname_to_open, O_RDONLY, 0);
  2980.         if (fd == -1) {
  2981.             if (errno == ENOENT) {
  2982.                 enum logcode c = am_daemon
  2983.                     && protocol_version < 28 ? FERROR
  2984.                                  : FWARNING;
  2985.                 io_error |= IOERR_VANISHED;
  2986. -               rprintf(c, "file has vanished: %s\n",
  2987. +               rprintf(c, "send_files: file has vanished: %s\n",
  2988.                     full_fname(fname));
  2989.             } else {
  2990.                 io_error |= IOERR_GENERAL;
  2991. @@ -300,6 +352,41 @@
  2992.             continue;
  2993.         }
  2994.  
  2995. +       if (source_filter) {
  2996. +           int fd2;
  2997. +           pathjoin(tmp, MAXPATHLEN, source_filter_tmp, "rsync-filtered_sourceXXXXXX");
  2998. +           fd2 = mkstemp(tmp);
  2999. +           if (fd2 == -1) {
  3000. +               rprintf(FERROR, "mkstemp %s failed: %s\n",
  3001. +                   tmp, strerror(errno));
  3002. +           } else {
  3003. +               int status;
  3004. +               char *filter_argv_subst[MAX_FILTER_ARGS + 1];
  3005. +               enum filter_args_info fai = substitute_filter_args(filter_argv_subst, fname_to_open, tmp, filter_argv);
  3006. +               if (fai & fai_has_source) {
  3007. +                   close(fd);
  3008. +                   fd = -1;
  3009. +               }
  3010. +               if (fai & fai_has_dest) {
  3011. +                   close(fd2);
  3012. +                   fd2 = -1;
  3013. +               }
  3014. +               pid_t pid = run_filter_on_file(filter_argv_subst, fd2, fd);
  3015. +               if (fd >= 0) close(fd);
  3016. +               if (fd2 >= 0) close(fd2);
  3017. +               wait_process_with_flush(pid, &status);
  3018. +               if (status != 0) {
  3019. +                   rprintf(FERROR,
  3020. +                       "bypassing source filter %s; exited with code: %d\n",
  3021. +                       source_filter, status);
  3022. +                   fd = do_open(fname_to_open, O_RDONLY, 0);
  3023. +               } else {
  3024. +                   fd = do_open(tmp, O_RDONLY, 0);
  3025. +                   unlink_tmp = 1;
  3026. +               }
  3027. +           }
  3028. +       }
  3029. +
  3030.         /* map the local file */
  3031.         if (do_fstat(fd, &st) != 0) {
  3032.             io_error |= IOERR_GENERAL;
  3033. @@ -309,6 +396,8 @@
  3034.             exit_cleanup(RERR_PROTOCOL);
  3035.         }
  3036.  
  3037. +       fscache_insert(fname, st.st_size);
  3038. +
  3039.         if (st.st_size) {
  3040.             int32 read_size = MAX(s->blength * 3, MAX_MAP_SIZE);
  3041.             mbuf = map_file(fd, st.st_size, read_size, s->blength);
  3042. @@ -350,7 +439,13 @@
  3043.             }
  3044.         }
  3045.         close(fd);
  3046. +       if (unlink_tmp)
  3047. +           unlink(tmp);
  3048. +       *tmp = '\0';
  3049.  
  3050. +       if (unlink_ntstreams_tmp)
  3051. +           unlink(ntstreams_tmpname);
  3052. +
  3053.         free_sums(s);
  3054.  
  3055.         if (verbose > 2)
  3056. Index: util.c
  3057. ===================================================================
  3058. --- util.c  (revision 2)
  3059. +++ util.c  (working copy)
  3060. @@ -465,6 +465,7 @@
  3061.  static int num_pids;
  3062.  
  3063.  /** Fork and record the pid of the child. **/
  3064. +/** NOTE: don't call this more than 10 times!!! - david.overton **/
  3065.  pid_t do_fork(void)
  3066.  {
  3067.     pid_t newpid = fork();
  3068. @@ -1701,3 +1702,73 @@
  3069.     }
  3070.     return (char*)lp->items + (lp->count++ * item_size);
  3071.  }
  3072. +
  3073. +char *my_strtok(char *s)
  3074. +{
  3075. +   static char *p;
  3076. +   char *tok;
  3077. +   int in_quote = 0;
  3078. +   if (s)
  3079. +       p = s;
  3080. +   if (!p || !*p)
  3081. +       return NULL;
  3082. +
  3083. +   if (*p == '\'' || *p == '"') {
  3084. +       in_quote = 1;
  3085. +       *p++ = '\0';
  3086. +   }
  3087. +   for (tok = p; *p; ++p) {
  3088. +       if ((!in_quote && (*p == ' ' || *p == '\t')) || (in_quote && (*p == '\'' || *p == '"'))) {
  3089. +           // We found the end of this token so clobber any remaining spaces and return the token.
  3090. +           do {
  3091. +               *p++ = '\0';
  3092. +           } while (*p == ' ' || *p == '\t');
  3093. +           break;
  3094. +       }
  3095. +   }
  3096. +   return tok;
  3097. +}
  3098. +
  3099. +void tokenize_filter_args(
  3100. +       /* OUT     */ char *filter_argv[],
  3101. +       /* IN/OUT  */ char *filter,
  3102. +       /* IN      */ const char *filter_type)
  3103. +{
  3104. +   char *p;
  3105. +   int i;
  3106. +   for (p = my_strtok(filter), i = 0;
  3107. +        p && i < MAX_FILTER_ARGS;
  3108. +        p = my_strtok(0))
  3109. +       filter_argv[i++] = p;
  3110. +   filter_argv[i] = NULL;
  3111. +   if (p) {
  3112. +       rprintf(FERROR,
  3113. +           "Too many arguments to %s-filter (> %d)\n",
  3114. +           filter_type, MAX_FILTER_ARGS);
  3115. +       exit_cleanup(RERR_SYNTAX);
  3116. +   }
  3117. +}
  3118. +
  3119. +enum filter_args_info substitute_filter_args(
  3120. +       /* OUT */ char *filter_argv_subst[],
  3121. +       /* IN  */ char *source_fname,
  3122. +       /* IN  */ char *dest_fname,
  3123. +       /* IN  */ char *filter_argv[])
  3124. +{
  3125. +   int i;
  3126. +   enum filter_args_info fai = fai_has_none;
  3127. +
  3128. +   for (i = 0; filter_argv[i] && i < MAX_FILTER_ARGS; ++i) {
  3129. +       if (strcmp(filter_argv[i], "%s") == 0) {
  3130. +           filter_argv_subst[i] = source_fname;
  3131. +           fai |= fai_has_source;
  3132. +       } else if (strcmp(filter_argv[i], "%d") == 0) {
  3133. +           filter_argv_subst[i] = dest_fname;
  3134. +           fai |= fai_has_dest;
  3135. +       } else {
  3136. +           filter_argv_subst[i] = filter_argv[i];
  3137. +       }
  3138. +   }
  3139. +   filter_argv_subst[i] = NULL;
  3140. +   return fai;
  3141. +}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement