예제 #1
0
static void
ct_version_tree_free_version(struct ct_vertree_ver *entry)
{
    struct ct_vertree_dir		*dir;
    struct ct_vertree_spec		*spec;
    struct ct_vertree_link		*flink;
    struct ct_vertree_file		*file;

    if (C_ISDIR(entry->cvv_type)) {
        dir = (struct ct_vertree_dir *)entry;
        e_free(&dir);
    } else if (C_ISBLK(entry->cvv_type) ||
               C_ISCHR(entry->cvv_type)) {
        spec = (struct ct_vertree_spec *)entry;
        e_free(&spec);
    } else  if (C_ISLINK(entry->cvv_type)) {
        flink = (struct ct_vertree_link *)entry;
        if (flink->cvl_linkname != NULL)
            e_free(&flink->cvl_linkname);
        e_free(&flink);
    } else if (C_ISREG(entry->cvv_type)) {
        file = (struct ct_vertree_file *)entry;
        e_free(&file);
    }

    return;
}
예제 #2
0
/*
 * So that we can provide correct statistics we have to go through all ctfiles
 * being extracted and sum the sizes to be extracted. This is kinda expensive,
 * but not really avoidable if we want to provide the statistics.
 *
 * Failure means we have called ct fatal.
 */
int
ct_extract_calculate_total(struct ct_global_state *state,
    struct ct_extract_args *cea, struct ct_match *inc_match,
    struct ct_match *ex_match)
{
	struct ct_extract_head		 extract_head;
	struct ctfile_parse_state	 xdr_ctx;
	struct ct_match			*rb_match = NULL;
	struct fnode			*fnode;
	int				 allfiles;
	int				 fillrb = 0, haverb = 0;
	int				 doextract = 0;
	int				 tr_state;
	int				 ret;
	int				 retval = 1;

	TAILQ_INIT(&extract_head);

	if ((ret = ct_extract_setup(&extract_head,
	    &xdr_ctx, cea->cea_local_ctfile, cea->cea_ctfile_basedir,
	    &allfiles)) != 0) {
		ct_fatal(state, "can't setup extract queue", ret);
		goto done;
	}
	if (allfiles) {
		char *nothing = NULL;
		if ((ret = ct_match_compile(&rb_match,
		    CT_MATCH_RB, &nothing)) != 0) {
			ct_fatal(state, "Couldn't create match tree",
			    ret);
			goto done;
		}
		fillrb = 1;
	}

	while (1) {
		switch ((ret = ctfile_parse(&xdr_ctx))) {
		case XS_RET_FILE:
			if (fillrb == 0 && xdr_ctx.xs_hdr.cmh_nr_shas == -1) {
				continue;
			}

			fnode = ct_alloc_fnode();
			/* XXX need the fnode for the correct paths */
			ct_populate_fnode(state->extract_state,
			    &xdr_ctx, fnode, &tr_state, allfiles,
			    cea->cea_strip_slash);
			/* we don't care about individual shas */
			if (C_ISREG(fnode->fn_type)) {
				ctfile_parse_seek(&xdr_ctx);
			}

			doextract = !ct_match(inc_match,
			    fnode->fn_fullname);
			if (doextract && ex_match != NULL &&
			  !ct_match(ex_match, fnode->fn_fullname))
				doextract = 0;
			/*
			 * If we're on the first ctfile in an allfiles backup
			 * put the matches with -1 on the rb tree so we'll
			 * remember to extract it from older files.
			 */
			if (doextract == 1 && fillrb &&
			    xdr_ctx.xs_hdr.cmh_nr_shas == -1) {
				ct_match_insert_rb(rb_match, fnode->fn_fullname);
				doextract = 0;
			}
			ct_free_fnode(fnode);
			break;
		case XS_RET_FILE_END:
			if (doextract == 0)
				continue;
			/* update statistics */
			state->ct_stats->st_bytes_tot +=
			    xdr_ctx.xs_trl.cmt_orig_size;
			break;
		case XS_RET_EOF:
			ctfile_parse_close(&xdr_ctx);
			/* if rb tree and rb is empty, goto end state */
			if ((haverb && ct_match_rb_is_empty(inc_match)) ||
			    (fillrb && ct_match_rb_is_empty(rb_match))) {
				retval = 0;
				goto done;
			}

			if (!TAILQ_EMPTY(&extract_head)) {
				/*
				 * if allfiles and this was the first pass.
				 * free the current match lists
				 * switch to rb tree mode
				 */
				if (fillrb) {
					ex_match = NULL;
					inc_match = rb_match;
					rb_match = NULL;
					haverb = 1;
					fillrb = 0;
				}
				/* reinits xdr_ctx */
				if ((ret = ct_extract_open_next(&extract_head,
				    &xdr_ctx)) != 0) {
					ct_fatal(state,
					    "Can't open next ctfile", ret);
					goto done;
				}
				continue;
			}
			retval = 0;
			goto done;
			break;
		case XS_RET_FAIL:
			ct_fatal(state, "Failed to parse ctfile",
			    xdr_ctx.xs_errno);
			goto done;
			break;
		}
	}

done:
	/* empty unless we quit early */
	ct_extract_cleanup_queue(&extract_head);
	/* only have control of the rb tree we made */
	if (haverb)
		ct_match_unwind(inc_match);
	if (rb_match != NULL)
		ct_match_unwind(rb_match);
		
	return (retval);
}
예제 #3
0
/*
 * Main guts of  ctd_build_version_tree. Factored out to avoid deep nesting.
 * Insert or update an entry in the tree with the information received from
 * the ctfile.
 */
static int
ct_vertree_add(struct ct_vertree_dnode_cache *dnode_cache,
               struct ct_vertree_entry *head, struct ctfile_parse_state *parse_state,
               struct ct_vertree_ctfile *ctfile, off_t fileoffset, int allfiles)
{
    struct ctfile_header		*hdr = &parse_state->xs_hdr;
    struct ctfile_header		*hdrlnk= &parse_state->xs_lnkhdr;
    struct dnode 			*dnode;
    struct ct_vertree_dnode		*fb_dnode;
    struct ct_vertree_entry		*parent = NULL, sentry, *entry;
    struct ct_vertree_ver		*lastver, *ver;
    struct ct_vertree_file		*file;
    struct ct_vertree_spec		*spec;
    struct ct_vertree_link		*linkver;
    size_t				 sz;
    bool				 root_dnode = false;

    entry = NULL;

    /* First find parent directory if any */
    if (hdr->cmh_parent_dir != -1 && hdr->cmh_parent_dir != -2) {
        if ((dnode = ctfile_parse_finddir(parse_state,
                                          hdr->cmh_parent_dir)) == NULL) {
            CNDBG(CT_LOG_VERTREE, "can't find dir %" PRId64,
                  hdr->cmh_parent_dir);
            return (CTE_CTFILE_CORRUPT);
        }
        fb_dnode = (struct ct_vertree_dnode *)dnode;
        if (fb_dnode == dnode_cache->root_dnode) {
            // If we have the root dnode, store in head.
            parent = head;
        } else {
            parent = fb_dnode->cvd_dir;
        }

    } else {
        parent = head;
    }

    if (parent == head && strcmp(hdr->cmh_filename, CT_PATHSEP_STR) == 0) {
        root_dnode = true;
    }
    /*
     * Have parent node, search children to see if we already exist.
     * Else make a new one and insert.
     */
    sentry.cve_name = hdr->cmh_filename;
    if ((entry = RB_FIND(ct_vertree_entries, &parent->cve_children,
                         &sentry)) == NULL) {
        /* new name, insert node */
        entry = e_calloc(1, sizeof(*entry));

        TAILQ_INIT(&entry->cve_versions);
        RB_INIT(&entry->cve_children);
        entry->cve_parent = parent;
        entry->cve_name = e_strdup(sentry.cve_name);

        /* don't insert root dnodes, just do dnode dance */
        if (root_dnode) {
            goto rootdir;
        }

        if (RB_INSERT(ct_vertree_entries, &parent->cve_children,
                      entry) != NULL) {
            CNDBG(CT_LOG_VERTREE, "entry %s already exists",
                  sentry.cve_name);
            e_free(&sentry.cve_name);
            goto err;
        }
    }

    /*
     * then check version tags -> head/tail if mtime and type match, we're
     * good else prepare version entry.
     */
    if (allfiles) {
        lastver = TAILQ_FIRST(&entry->cve_versions);
    } else {
        lastver = TAILQ_LAST(&entry->cve_versions, ct_vertree_vers);
    }
    /* Don't check atime, it doesn't matter */
    if (lastver != NULL && lastver->cvv_type == hdr->cmh_type &&
            lastver->cvv_mtime == hdr->cmh_mtime &&
            lastver->cvv_uid == hdr->cmh_uid &&
            lastver->cvv_gid == hdr->cmh_gid &&
            lastver->cvv_mode == hdr->cmh_mode) {
        ver = lastver;
    } else { /* something changed. make a new one */
        if (C_ISDIR(hdr->cmh_type)) {
            sz = sizeof(struct ct_vertree_dir);
        } else if (C_ISBLK(hdr->cmh_type) ||
                   C_ISCHR(hdr->cmh_type)) {
            sz = sizeof(struct ct_vertree_spec);
        } else  if (C_ISLINK(hdr->cmh_type)) {
            sz = sizeof(struct ct_vertree_link);
        } else if (C_ISREG(hdr->cmh_type)) {
            sz = sizeof(struct ct_vertree_file);
        } else {
            CNDBG(CT_LOG_VERTREE, "invalid type %d", hdr->cmh_type);
            goto err;
        }
        ver = e_calloc(1, sz);
        ver->cvv_type = hdr->cmh_type;
        ver->cvv_mtime = hdr->cmh_mtime;
        ver->cvv_atime = hdr->cmh_atime;
        ver->cvv_uid = hdr->cmh_uid;
        ver->cvv_gid = hdr->cmh_gid;
        ver->cvv_mode = hdr->cmh_mode;
        /* dir handled below */
        if (C_ISBLK(hdr->cmh_type) || C_ISCHR(hdr->cmh_type))  {
            spec = (struct ct_vertree_spec *)ver;
            spec->cvs_rdev = hdr->cmh_rdev;
        } else if (C_ISLINK(hdr->cmh_type)) {
            /* hardlink/symlink */
            linkver = (struct ct_vertree_link *)ver;
            linkver->cvl_linkname = e_strdup(hdrlnk->cmh_filename);
            linkver->cvl_hardlink = !C_ISLINK(hdrlnk->cmh_type);
        } else if (C_ISREG(hdr->cmh_type)) {
            file = (struct ct_vertree_file *)ver;
            file->cvf_nr_shas = -1;
        }
        if (allfiles) {
            TAILQ_INSERT_HEAD(&entry->cve_versions, ver, cvv_link);
        } else {
            TAILQ_INSERT_TAIL(&entry->cve_versions, ver, cvv_link);
        }
    }


    /*
     * Each ctfile only has each directory referenced once, so put it
     * in the cache regardless of whether it was known of before, that
     * will be a previous run and the cache will have been wiped since
     * then.
     */
    if (C_ISDIR(hdr->cmh_type)) {
rootdir:
        fb_dnode = e_calloc(1, sizeof(*fb_dnode));
        fb_dnode->cvd_dnode.d_name = e_strdup(entry->cve_name);
        /*
         * in the root_dnode case this will be a bad pointer but it
         * will never be derefed.
         */
        fb_dnode->cvd_dir = entry;
        if ((dnode = ctfile_parse_insertdir(parse_state,
                                            &fb_dnode->cvd_dnode)) != NULL)
            CABORTX("duplicate dentry");
        TAILQ_INSERT_TAIL(&dnode_cache->cache, fb_dnode, cvd_link);
        if (root_dnode) {
            dnode_cache->root_dnode = fb_dnode;
        }
    } else if (C_ISREG(hdr->cmh_type)) {
        /*
         * Allfiles ctfiles may have shas == -1, so in some cases we
         * may wish to update an existing file when we find the actual
         * shas. It is an error to have a file node with -1 for shas
         * after all metadata have been parsed. it means one was
         * missing.
         */
        file = (struct ct_vertree_file *)ver;

        /*
         * bugs in previous editions with incremental selection and
         * off_t on linux mean that there are ctfiles in the wild which
         * provide a list of shas in a later level when the file is
         * defined in an earlier level file, also. For example for the
         * same filename and date we have level 0: 3 shas, level 1: -1
         * shas (i.e. in a previous level), level 2: 3 shas (same as
         * level * 0). In that case we just assume that if we already
         * have sha data for a file * then it is correct and we skip
         * previous versions.
         */
        if (file->cvf_nr_shas != -1) {
            goto out;
        }

        /*
         * previous linux off_t bugs with files over 2gb mean that there
         * are sign extended ctfiles in the wild, so we count those as
         * zero length for purposes of the version tree.
         */
        if (hdr->cmh_nr_shas < -1) {
            hdr->cmh_nr_shas = 0;
        }
        if (hdr->cmh_nr_shas != -1)  {
            file->cvf_nr_shas = hdr->cmh_nr_shas;
            file->cvf_sha_offs = fileoffset;
            file->cvf_file = ctfile;
            if (ctfile_parse_seek(parse_state)) {
                CNDBG(CT_LOG_VERTREE,
                      "failed to skip shas in %s",
                      ctfile->cvc_path);
                goto err;
            }
        }
        if (ctfile_parse(parse_state) != XS_RET_FILE_END) {
            CNDBG(CT_LOG_VERTREE, "no file trailer found");
            goto err;
        }

        file->cvf_file_size = parse_state->xs_trl.cmt_orig_size;
    }

out:
    /*
     * If we're an explicit "/" entry then we don't want to be added to
     * the tree. all our children will be added to the root entry.
     */
    if (root_dnode) {
        e_free(&entry);
    }
    return (0);

err:
    if (entry != NULL)
        e_free(&entry);
    return (CTE_CTFILE_CORRUPT);
}
예제 #4
0
파일: ct_main.c 프로젝트: finid/cyphertite
/* Printing functions */
void
ct_pr_fmt_file(void *state, struct fnode *fnode)
{
	int		*verbose = state;
	char		*loginname;
	struct group	*group;
	char		*link_ty, *pchr;
	char		 filemode[11], uid[11], gid[11], lctime[26];
	time_t		 ltime;

	if (*verbose == 0)
		return;

	if (*verbose > 1) {
		switch(fnode->fn_type & C_TY_MASK) {
		case C_TY_DIR:
			filemode[0] = 'd'; break;
		case C_TY_CHR:
			filemode[0] = 'c'; break;
		case C_TY_BLK:
			filemode[0] = 'b'; break;
		case C_TY_REG:
			filemode[0] = '-'; break;
		case C_TY_FIFO:
			filemode[0] = 'f'; break;
		case C_TY_LINK:
			filemode[0] = 'l'; break;
		case C_TY_SOCK:
			filemode[0] = 's'; break;
		default:
			filemode[0] = '?';
		}
		filemode[1] = (fnode->fn_mode & 0400) ? 'r' : '-';
		filemode[2] = (fnode->fn_mode & 0100) ? 'w' : '-';
		filemode[3] = (fnode->fn_mode & 0200) ? 'x' : '-';
		filemode[4] = (fnode->fn_mode & 0040) ? 'r' : '-';
		filemode[5] = (fnode->fn_mode & 0020) ? 'w' : '-';
		filemode[6] = (fnode->fn_mode & 0010) ? 'x' : '-';
		filemode[7] = (fnode->fn_mode & 0004) ? 'r' : '-';
		filemode[8] = (fnode->fn_mode & 0002) ? 'w' : '-';
		filemode[9] = (fnode->fn_mode & 0001) ? 'x' : '-';
		filemode[10] = '\0';

		loginname = ct_getloginbyuid(fnode->fn_uid);
		if (loginname && (strlen(loginname) < sizeof(uid)))
			snprintf(uid, sizeof(uid), "%10s", loginname);
		else
			snprintf(uid, sizeof(uid), "%-10d", fnode->fn_uid);
		group = getgrgid(fnode->fn_gid);
		if (group && (strlen(group->gr_name) < sizeof(gid)))
			snprintf(gid, sizeof(gid), "%10s", group->gr_name);
		else
			snprintf(gid, sizeof(gid), "%-10d", fnode->fn_gid);
		ltime = fnode->fn_mtime;
		ctime_r(&ltime, lctime);
		pchr = strchr(lctime, '\n');
		if (pchr != NULL)
			*pchr = '\0'; /* stupid newline on ctime */

		printf("%s %s %s %s ", filemode, uid, gid, lctime);
	}
	if (fnode->fn_skip_file) {
		if (*verbose > 1)
			printf("%s does not need rearchive",
			    fnode->fn_fullname);
	} else {
		printf("%s", fnode->fn_fullname);
	}

	if (*verbose > 1) {
		/* XXX - translate to guid name */
		if (C_ISLINK(fnode->fn_type))  {
			if (fnode->fn_hardlink)  {
				link_ty = "==";
			} else {
				link_ty = "->";
			}
			printf(" %s %s", link_ty, fnode->fn_hlname);
		} else if (C_ISREG(fnode->fn_type)) {
		}
	}
	fflush(stdout);
}
예제 #5
0
파일: ct_main.c 프로젝트: finid/cyphertite
int
ct_list(const char *file, char **flist, char **excludelist, int match_mode,
    const char *ctfile_basedir, int strip_slash, int verbose)
{
	struct ct_extract_state		*ces;
	struct ctfile_parse_state	 xs_ctx;
	struct fnode			 fnodestore;
	uint64_t			 reduction;
	struct fnode			*fnode = &fnodestore;
	struct ct_match			*match, *ex_match = NULL;
	char				*ct_next_filename;
	char				*sign;
	int				 state;
	int				 doprint = 0;
	int				 ret;
	int				 s_errno = 0, ct_errno = 0;
	char				 shat[SHA_DIGEST_STRING_LENGTH];
	char				 cshat[SHA_DIGEST_STRING_LENGTH];
	char				 iv[CT_IV_LEN*2+1];

	if ((ret = ct_file_extract_init(&ces, NULL, 1, 1, 0, NULL, NULL)) != 0)
		CFATALX("failed to initialise extract state: %s",
		    ct_strerror(ret));
	if ((ret = ct_match_compile(&match, match_mode, flist)) != 0)
		CFATALX("failed to compile match pattern: %s",
		    ct_strerror(ret));
	if (excludelist != NULL && (ret = ct_match_compile(&ex_match,
	    match_mode, excludelist)) != 0)
		CFATALX("failed to compile exclude pattern: %s",
		    ct_strerror(ret));

	verbose++;	/* by default print something. */

	ct_next_filename = NULL;
next_file:
	ret = ctfile_parse_init(&xs_ctx, file, ctfile_basedir);
	if (ret)
		CFATALX("failed to open %s: %s", file, ct_strerror(ret));
	ct_print_ctfile_info(&verbose, file, &xs_ctx.xs_gh);

	if (ct_next_filename)
		e_free(&ct_next_filename);

	if (xs_ctx.xs_gh.cmg_prevlvl_filename) {
		CNDBG(CT_LOG_CTFILE, "previous backup file %s\n",
		    xs_ctx.xs_gh.cmg_prevlvl_filename);
		ct_next_filename = e_strdup(xs_ctx.xs_gh.cmg_prevlvl_filename);
	}
	bzero(&fnodestore, sizeof(fnodestore));

	do {
		ret = ctfile_parse(&xs_ctx);
		switch (ret) {
		case XS_RET_FILE:
			ct_populate_fnode(ces, &xs_ctx, fnode, &state,
			    xs_ctx.xs_gh.cmg_flags & CT_MD_MLB_ALLFILES,
			    strip_slash);
			doprint = !ct_match(match, fnode->fn_fullname);
			if (doprint && ex_match != NULL &&
			    !ct_match(ex_match, fnode->fn_fullname))
				doprint = 0;
			if (doprint) {
				ct_pr_fmt_file(&verbose, fnode);
				if (!C_ISREG(xs_ctx.xs_hdr.cmh_type) ||
				    verbose > 2)
					printf("\n");
			}
			if (fnode->fn_hlname)
				e_free(&fnode->fn_hlname);
			if (fnode->fn_fullname)
				e_free(&fnode->fn_fullname);
			break;
		case XS_RET_FILE_END:
			sign = " ";
			if (xs_ctx.xs_trl.cmt_comp_size == 0)
				reduction = 100;
			else {
				uint64_t orig, comp;
				orig = xs_ctx.xs_trl.cmt_orig_size;
				comp = xs_ctx.xs_trl.cmt_comp_size;

				if (comp <= orig) {
					reduction = 100 * (orig - comp) / orig;
				} else  {
					reduction = 100 * (comp - orig) / orig;
					if (reduction != 0)
						sign = "-";
				}
			}
			if (doprint && verbose > 1)
				printf(" sz: %" PRIu64 " shas: %" PRIu64
				    " reduction: %s%" PRIu64 "%%\n",
				    xs_ctx.xs_trl.cmt_orig_size,
				    xs_ctx.xs_hdr.cmh_nr_shas,
				    sign, reduction);
			else if (doprint)
				printf("\n");
			break;
		case XS_RET_SHA:
			if (!(doprint && verbose > 2)) {
				if (ctfile_parse_seek(&xs_ctx)) {
					CFATALX("seek failed");
				}
			} else {
				int i;
				ct_sha1_encode(xs_ctx.xs_sha, shat);
				switch (xs_ctx.xs_gh.cmg_flags & CT_MD_CRYPTO) {
				case 0:
					printf(" sha %s\n", shat);
					break;
				case CT_MD_CRYPTO:
					ct_sha1_encode(xs_ctx.xs_csha, cshat);
					for (i = 0; i < CT_IV_LEN; i++)
						snprintf(&iv[i * 2], 3, "%02x",
						    xs_ctx.xs_iv[i]);

					printf(" sha %s csha %s iv %s\n",
					    shat, cshat, iv);
				}
			}
			break;
		case XS_RET_EOF:
			break;
		case XS_RET_FAIL:
			s_errno = errno;
			ct_errno = xs_ctx.xs_errno;
			;
		}

	} while (ret != XS_RET_EOF && ret != XS_RET_FAIL);

	ctfile_parse_close(&xs_ctx);

	if (ret != XS_RET_EOF) {
		errno = s_errno;
		CWARNX("corrupt ctfile: %s", ct_strerror(ct_errno));
	} else {
		if (ct_next_filename) {
			file = ct_next_filename;
			goto next_file;
		}
	}
	ct_match_unwind(match);
	ct_file_extract_cleanup(ces);
	return (0);
}