+ while (bnet_recv(dir) >= 0) {
+ dir->msg[dir->msglen] = 0;
+ strip_trailing_junk(dir->msg);
+ Dmsg1(010, "include file: %s\n", dir->msg);
+ add_fname_to_list(jcr, dir->msg, INC_LIST);
+ }
+
+ return bnet_fsend(dir, OKinc);
+}
+
+static bool init_fileset(JCR *jcr)
+{
+ FF_PKT *ff;
+ findFILESET *fileset;
+
+ if (!jcr->ff) {
+ return false;
+ }
+ ff = (FF_PKT *)jcr->ff;
+ if (ff->fileset) {
+ return false;
+ }
+ fileset = (findFILESET *)malloc(sizeof(findFILESET));
+ memset(fileset, 0, sizeof(findFILESET));
+ ff->fileset = fileset;
+ fileset->state = state_none;
+ fileset->include_list.init(1, true);
+ fileset->exclude_list.init(1, true);
+ return true;
+}
+
+static findFOPTS *start_options(FF_PKT *ff)
+{
+ int state = ff->fileset->state;
+ findINCEXE *incexe = ff->fileset->incexe;
+
+ if (state != state_options) {
+ ff->fileset->state = state_options;
+ findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS));
+ memset(fo, 0, sizeof(findFOPTS));
+ fo->regex.init(1, true);
+ fo->regexdir.init(1, true);
+ fo->regexfile.init(1, true);
+ fo->wild.init(1, true);
+ fo->wilddir.init(1, true);
+ fo->wildfile.init(1, true);
+ fo->base.init(1, true);
+ fo->fstype.init(1, true);
+ incexe->current_opts = fo;
+ incexe->opts_list.append(fo);
+ }
+ return incexe->current_opts;
+
+}
+
+/*
+ * Add fname to include/exclude fileset list. First check for
+ * | and < and if necessary perform command.
+ */
+static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *fileset)
+{
+ char *p;
+ BPIPE *bpipe;
+ POOLMEM *fn;
+ FILE *ffd;
+ char buf[1000];
+ int stat;
+
+ p = (char *)fname;
+ switch (*p) {
+ case '|':
+ p++; /* skip over | */
+ fn = get_pool_memory(PM_FNAME);
+ fn = edit_job_codes(jcr, fn, p, "");
+ bpipe = open_bpipe(fn, 0, "r");
+ free_pool_memory(fn);
+ if (!bpipe) {
+ Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
+ p, strerror(errno));
+ return;
+ }
+ while (fgets(buf, sizeof(buf), bpipe->rfd)) {
+ strip_trailing_junk(buf);
+ fileset->incexe->name_list.append(bstrdup(buf));
+ }
+ if ((stat=close_bpipe(bpipe)) != 0) {
+ Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"),
+ p, stat, strerror(errno));
+ return;
+ }
+ break;
+ case '<':
+ p++; /* skip over < */
+ if ((ffd = fopen(p, "r")) == NULL) {
+ berrno be;
+ Jmsg(jcr, M_FATAL, 0, _("Cannot open FileSet input file: %s. ERR=%s\n"),
+ p, be.strerror());
+ return;
+ }
+ while (fgets(buf, sizeof(buf), ffd)) {
+ strip_trailing_junk(buf);
+ fileset->incexe->name_list.append(bstrdup(buf));
+ }
+ fclose(ffd);
+ break;
+ default:
+ fileset->incexe->name_list.append(bstrdup(fname));
+ break;
+ }
+}
+
+
+static void add_fileset(JCR *jcr, const char *item)
+{
+ FF_PKT *ff = (FF_PKT *)jcr->ff;
+ findFILESET *fileset = ff->fileset;
+ int state = fileset->state;
+ findFOPTS *current_opts;
+
+ /* Get code, optional subcode, and position item past the dividing space */
+ Dmsg1(100, "%s\n", item);
+ int code = item[0];
+ if (code != '\0') {
+ ++item;
+ }
+ int subcode = ' '; /* A space is always a valid subcode */
+ if (item[0] != '\0' && item[0] != ' ') {
+ subcode = item[0];
+ ++item;
+ }
+ if (*item == ' ') {
+ ++item;
+ }
+
+ /* Skip all lines we receive after an error */
+ if (state == state_error) {
+ return;
+ }
+
+ /*
+ * The switch tests the code for validity.
+ * The subcode is always good if it is a space, otherwise we must confirm.
+ * We set state to state_error first assuming the subcode is invalid,
+ * requiring state to be set in cases below that handle subcodes.
+ */
+ if (subcode != ' ') {
+ state = state_error;
+ }
+ switch (code) {
+ case 'I':
+ /* New include */
+ fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
+ memset(fileset->incexe, 0, sizeof(findINCEXE));
+ fileset->incexe->opts_list.init(1, true);
+ fileset->incexe->name_list.init(1, true);
+ fileset->include_list.append(fileset->incexe);
+ break;
+ case 'E':
+ /* New exclude */
+ fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
+ memset(fileset->incexe, 0, sizeof(findINCEXE));
+ fileset->incexe->opts_list.init(1, true);
+ fileset->incexe->name_list.init(1, true);
+ fileset->exclude_list.append(fileset->incexe);
+ break;
+ case 'N':
+ state = state_none;
+ break;
+ case 'F':
+ /* File item to either include/include list */
+ state = state_include;
+ add_file_to_fileset(jcr, item, fileset);
+ break;
+ case 'R':
+ current_opts = start_options(ff);
+ regex_t *preg;
+ int rc;
+ char prbuf[500];
+ preg = (regex_t *)malloc(sizeof(regex_t));
+ if (current_opts->flags & FO_IGNORECASE) {
+ rc = regcomp(preg, item, REG_EXTENDED|REG_ICASE);
+ } else {
+ rc = regcomp(preg, item, REG_EXTENDED);
+ }
+ if (rc != 0) {
+ regerror(rc, preg, prbuf, sizeof(prbuf));
+ regfree(preg);
+ free(preg);
+ Jmsg(jcr, M_FATAL, 0, "REGEX %s compile error. ERR=%s\n", item, prbuf);
+ state = state_error;
+ break;
+ }
+ state = state_options;
+ if (subcode == ' ') {
+ current_opts->regex.append(preg);
+ } else if (subcode == 'D') {
+ current_opts->regexdir.append(preg);
+ } else if (subcode == 'F') {
+ current_opts->regexfile.append(preg);
+ } else {
+ state = state_error;
+ }
+ break;
+ case 'B':
+ current_opts = start_options(ff);
+ current_opts->base.append(bstrdup(item));
+ state = state_options;
+ break;
+ case 'X':
+ current_opts = start_options(ff);
+ current_opts->fstype.append(bstrdup(item));
+ state = state_options;
+ break;
+ case 'W':
+ current_opts = start_options(ff);
+ state = state_options;
+ if (subcode == ' ') {
+ current_opts->wild.append(bstrdup(item));
+ } else if (subcode == 'D') {
+ current_opts->wilddir.append(bstrdup(item));
+ } else if (subcode == 'F') {
+ current_opts->wildfile.append(bstrdup(item));
+ } else {
+ state = state_error;
+ }
+ break;
+ case 'O':
+ current_opts = start_options(ff);
+ set_options(current_opts, item);
+ state = state_options;
+ break;
+ case 'D':
+ current_opts = start_options(ff);
+ current_opts->reader = bstrdup(item);
+ state = state_options;
+ break;
+ case 'T':
+ current_opts = start_options(ff);
+ current_opts->writer = bstrdup(item);
+ state = state_options;
+ break;
+ default:
+ Jmsg(jcr, M_FATAL, 0, "Invalid FileSet command: %s\n", item);
+ state = state_error;
+ break;
+ }
+ ff->fileset->state = state;
+}
+
+static bool term_fileset(JCR *jcr)
+{
+ FF_PKT *ff = (FF_PKT *)jcr->ff;
+ findFILESET *fileset = ff->fileset;
+ int i, j, k;
+
+ for (i=0; i<fileset->include_list.size(); i++) {
+ findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
+ Dmsg0(400, "I\n");
+ for (j=0; j<incexe->opts_list.size(); j++) {
+ findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
+ for (k=0; k<fo->regex.size(); k++) {
+ Dmsg1(400, "R %s\n", (char *)fo->regex.get(k));
+ }
+ for (k=0; k<fo->regexdir.size(); k++) {
+ Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k));
+ }
+ for (k=0; k<fo->regexfile.size(); k++) {
+ Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k));
+ }
+ for (k=0; k<fo->wild.size(); k++) {
+ Dmsg1(400, "W %s\n", (char *)fo->wild.get(k));
+ }
+ for (k=0; k<fo->wilddir.size(); k++) {
+ Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k));
+ }
+ for (k=0; k<fo->wildfile.size(); k++) {
+ Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k));
+ }
+ for (k=0; k<fo->base.size(); k++) {
+ Dmsg1(400, "B %s\n", (char *)fo->base.get(k));
+ }
+ for (k=0; k<fo->fstype.size(); k++) {
+ Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k));
+ }
+ if (fo->reader) {
+ Dmsg1(400, "D %s\n", fo->reader);
+ }
+ if (fo->writer) {
+ Dmsg1(400, "T %s\n", fo->writer);
+ }
+ }
+ for (j=0; j<incexe->name_list.size(); j++) {
+ Dmsg1(400, "F %s\n", (char *)incexe->name_list.get(j));
+ }
+ }
+ for (i=0; i<fileset->exclude_list.size(); i++) {
+ findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
+ Dmsg0(400, "E\n");
+ for (j=0; j<incexe->opts_list.size(); j++) {
+ findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
+ for (k=0; k<fo->regex.size(); k++) {
+ Dmsg1(400, "R %s\n", (char *)fo->regex.get(k));
+ }
+ for (k=0; k<fo->regexdir.size(); k++) {
+ Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k));
+ }
+ for (k=0; k<fo->regexfile.size(); k++) {
+ Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k));
+ }
+ for (k=0; k<fo->wild.size(); k++) {
+ Dmsg1(400, "W %s\n", (char *)fo->wild.get(k));
+ }
+ for (k=0; k<fo->wilddir.size(); k++) {
+ Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k));
+ }
+ for (k=0; k<fo->wildfile.size(); k++) {
+ Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k));
+ }
+ for (k=0; k<fo->base.size(); k++) {
+ Dmsg1(400, "B %s\n", (char *)fo->base.get(k));
+ }
+ for (k=0; k<fo->fstype.size(); k++) {
+ Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k));
+ }
+ }
+ for (j=0; j<incexe->name_list.size(); j++) {
+ Dmsg1(400, "F %s\n", (char *)incexe->name_list.get(j));
+ }
+ }
+ return ff->fileset->state != state_error;
+}
+
+
+/*
+ * As an optimization, we should do this during
+ * "compile" time in filed/job.c, and keep only a bit mask
+ * and the Verify options.
+ */
+static void set_options(findFOPTS *fo, const char *opts)
+{
+ int j;
+ const char *p;
+
+ for (p=opts; *p; p++) {
+ switch (*p) {
+ case 'a': /* alway replace */
+ case '0': /* no option */
+ break;
+ case 'e':
+ fo->flags |= FO_EXCLUDE;
+ break;
+ case 'f':
+ fo->flags |= FO_MULTIFS;
+ break;
+ case 'h': /* no recursion */
+ fo->flags |= FO_NO_RECURSION;
+ break;
+ case 'H': /* no hard link handling */
+ fo->flags |= FO_NO_HARDLINK;
+ break;
+ case 'i':
+ fo->flags |= FO_IGNORECASE;
+ break;
+ case 'M': /* MD5 */
+ fo->flags |= FO_MD5;
+ break;
+ case 'n':
+ fo->flags |= FO_NOREPLACE;
+ break;
+ case 'p': /* use portable data format */
+ fo->flags |= FO_PORTABLE;
+ break;
+ case 'R': /* Resource forks and Finder Info */
+ fo->flags |= FO_HFSPLUS;
+ case 'r': /* read fifo */
+ fo->flags |= FO_READFIFO;
+ break;
+ case 'S':
+ fo->flags |= FO_SHA1;
+ break;
+ case 's':
+ fo->flags |= FO_SPARSE;
+ break;
+ case 'm':
+ fo->flags |= FO_MTIMEONLY;
+ break;
+ case 'k':
+ fo->flags |= FO_KEEPATIME;
+ break;
+ case 'A':
+ fo->flags |= FO_ACL;
+ break;
+ case 'V': /* verify options */
+ /* Copy Verify Options */
+ for (j=0; *p && *p != ':'; p++) {
+ fo->VerifyOpts[j] = *p;
+ if (j < (int)sizeof(fo->VerifyOpts) - 1) {
+ j++;
+ }
+ }
+ fo->VerifyOpts[j] = 0;
+ break;
+ case 'w':
+ fo->flags |= FO_IF_NEWER;
+ break;
+ case 'Z': /* gzip compression */
+ fo->flags |= FO_GZIP;
+ fo->GZIP_level = *++p - '0';
+ Dmsg1(200, "Compression level=%d\n", fo->GZIP_level);
+ break;
+ default:
+ Emsg1(M_ERROR, 0, "Unknown include/exclude option: %c\n", *p);
+ break;
+ }