+ push = (struct local_filter_state *)new_array(char,
+ sizeof (struct local_filter_state)
+ + (mergelist_cnt-1) * sizeof (struct filter_list_struct));
+ if (!push)
+ out_of_memory("push_local_filters");
+
+ push->mergelist_cnt = mergelist_cnt;
+ for (i = 0; i < mergelist_cnt; i++) {
+ memcpy(&push->mergelists[i], mergelist_parents[i]->u.mergelist,
+ sizeof (struct filter_list_struct));
+ }
+
+ /* Note: parse_filter_file() might increase mergelist_cnt, so keep
+ * this loop separate from the above loop. */
+ for (i = 0; i < mergelist_cnt; i++) {
+ struct filter_struct *ex = mergelist_parents[i];
+ struct filter_list_struct *lp = ex->u.mergelist;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] pushing mergelist #%d%s\n",
+ who_am_i(), i, lp->debug_type);
+ }
+
+ lp->tail = NULL; /* Switch any local rules to inherited. */
+ if (ex->match_flags & MATCHFLG_NO_INHERIT)
+ lp->head = NULL;
+
+ if (ex->match_flags & MATCHFLG_FINISH_SETUP) {
+ ex->match_flags &= ~MATCHFLG_FINISH_SETUP;
+ if (setup_merge_file(i, ex, lp))
+ set_filter_dir(dir, dirlen);
+ }
+
+ if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
+ MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len) {
+ parse_filter_file(lp, dirbuf, ex->match_flags,
+ XFLG_ANCHORED2ABS);
+ } else {
+ io_error |= IOERR_GENERAL;
+ rprintf(FERROR,
+ "cannot add local filter rules in long-named directory: %s\n",
+ full_fname(dirbuf));
+ }
+ dirbuf[dirbuf_len] = '\0';
+ }
+
+ return (void*)push;
+}
+
+void pop_local_filters(void *mem)
+{
+ struct local_filter_state *pop = (struct local_filter_state *)mem;
+ int i;
+ int old_mergelist_cnt = pop ? pop->mergelist_cnt : 0;
+
+ if (DEBUG_GTE(FILTER, 2))
+ rprintf(FINFO, "[%s] popping local filters\n", who_am_i());
+
+ for (i = mergelist_cnt; i-- > 0; ) {
+ struct filter_struct *ex = mergelist_parents[i];
+ struct filter_list_struct *lp = ex->u.mergelist;
+
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] popping mergelist #%d%s\n",
+ who_am_i(), i, lp->debug_type);
+ }
+
+ clear_filter_list(lp);
+
+ if (i >= old_mergelist_cnt) {
+ /* This mergelist does not exist in the state to be
+ * restored. Free its parent_dirscan list to clean up
+ * any per-dir mergelists defined there so we don't
+ * crash trying to restore nonexistent state for them
+ * below. (Counterpart to setup_merge_file call in
+ * push_local_filters. Must be done here, not in
+ * free_filter, for LIFO order.) */
+ if (DEBUG_GTE(FILTER, 2)) {
+ rprintf(FINFO, "[%s] freeing parent_dirscan filters of mergelist #%d%s\n",
+ who_am_i(), i, ex->u.mergelist->debug_type);
+ }
+ free_filters(lp->parent_dirscan_head);
+ lp->parent_dirscan_head = NULL;
+ }
+ }
+
+ /* If we cleaned things up properly, the only still-active mergelists
+ * should be those with a state to be restored. */
+ assert(mergelist_cnt == old_mergelist_cnt);
+
+ if (!pop) {
+ /* No state to restore. */
+ return;
+ }
+
+ for (i = 0; i < mergelist_cnt; i++) {
+ memcpy(mergelist_parents[i]->u.mergelist, &pop->mergelists[i],
+ sizeof (struct filter_list_struct));
+ }
+
+ free(pop);