/* * Copyright (c) 2008 Vreixo Formoso * * This file is part of the libisofs project; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See COPYING file for details. */ #include "libisofs.h" #include "node.h" #include #include struct iso_find_condition { /* * Check whether the given node matches this condition. * * @param cond * The condition to check * @param node * The node that should be checked * @return * 1 if the node matches the condition, 0 if not */ int (*matches)(IsoFindCondition *cond, IsoNode *node); /** * Free condition specific data */ void (*free)(IsoFindCondition*); /** condition specific data */ void *data; }; struct find_iter_data { IsoDirIter *iter; IsoFindCondition *cond; }; static int find_iter_next(IsoDirIter *iter, IsoNode **node) { int ret; IsoNode *n; struct find_iter_data *data = iter->data; while ((ret = iso_dir_iter_next(data->iter, &n)) == 1) { if (data->cond->matches(data->cond, n)) { *node = n; break; } } return ret; } static int find_iter_has_next(IsoDirIter *iter) { struct find_iter_data *data = iter->data; /* * FIXME wrong implementation!!!! the underlying iter may have more nodes, * but they may not match find conditions */ return iso_dir_iter_has_next(data->iter); } static void find_iter_free(IsoDirIter *iter) { struct find_iter_data *data = iter->data; data->cond->free(data->cond); free(data->cond); iso_dir_iter_free(data->iter); free(iter->data); } static int find_iter_take(IsoDirIter *iter) { struct find_iter_data *data = iter->data; return iso_dir_iter_take(data->iter); } static int find_iter_remove(IsoDirIter *iter) { struct find_iter_data *data = iter->data; return iso_dir_iter_remove(data->iter); } void find_notify_child_taken(IsoDirIter *iter, IsoNode *node) { /* nothing to do */ return; } static struct iso_dir_iter_iface find_iter_class = { find_iter_next, find_iter_has_next, find_iter_free, find_iter_take, find_iter_remove, find_notify_child_taken }; int iso_dir_find_children(IsoDir* dir, IsoFindCondition *cond, IsoDirIter **iter) { int ret; IsoDirIter *children; IsoDirIter *it; struct find_iter_data *data; if (dir == NULL || cond == NULL || iter == NULL) { return ISO_NULL_POINTER; } it = malloc(sizeof(IsoDirIter)); if (it == NULL) { return ISO_OUT_OF_MEM; } data = malloc(sizeof(struct find_iter_data)); if (data == NULL) { free(it); return ISO_OUT_OF_MEM; } ret = iso_dir_get_children(dir, &children); if (ret < 0) { free(it); free(data); return ret; } it->class = &find_iter_class; it->dir = (IsoDir*)dir; data->iter = children; data->cond = cond; it->data = data; if (iso_dir_iter_register(it) < 0) { free(it); return ISO_OUT_OF_MEM; } *iter = it; return ISO_SUCCESS; } /*************** find by name wildcard condition *****************/ static int cond_name_matches(IsoFindCondition *cond, IsoNode *node) { char *pattern = (char*) cond->data; int ret = fnmatch(pattern, node->name, 0); return ret == 0 ? 1 : 0; } static void cond_name_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks if the node name matches the given * wildcard. * * @param wildcard * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_name(const char *wildcard) { IsoFindCondition *cond; if (wildcard == NULL) { return NULL; } cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } cond->data = strdup(wildcard); cond->free = cond_name_free; cond->matches = cond_name_matches; return cond; } /*************** find by mode condition *****************/ static int cond_mode_matches(IsoFindCondition *cond, IsoNode *node) { mode_t *mask = (mode_t*) cond->data; return node->mode & *mask ? 1 : 0; } static void cond_mode_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the node mode against a mode mask. It * can be used to check both file type and permissions. * * For example: * * iso_new_find_conditions_mode(S_IFREG) : search for regular files * iso_new_find_conditions_mode(S_IFCHR | S_IWUSR) : search for character * devices where owner has write permissions. * * @param mask * Mode mask to AND against node mode. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_mode(mode_t mask) { IsoFindCondition *cond; mode_t *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(mode_t)); if (data == NULL) { free(cond); return NULL; } *data = mask; cond->data = data; cond->free = cond_mode_free; cond->matches = cond_mode_matches; return cond; } /*************** find by gid condition *****************/ static int cond_gid_matches(IsoFindCondition *cond, IsoNode *node) { gid_t *gid = (gid_t*) cond->data; return node->gid == *gid ? 1 : 0; } static void cond_gid_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the node gid. * * @param gid * Desired Group Id. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_gid(gid_t gid) { IsoFindCondition *cond; gid_t *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(gid_t)); if (data == NULL) { free(cond); return NULL; } *data = gid; cond->data = data; cond->free = cond_gid_free; cond->matches = cond_gid_matches; return cond; } /*************** find by uid condition *****************/ static int cond_uid_matches(IsoFindCondition *cond, IsoNode *node) { uid_t *uid = (uid_t*) cond->data; return node->uid == *uid ? 1 : 0; } static void cond_uid_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the node uid. * * @param uid * Desired User Id. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_uid(uid_t uid) { IsoFindCondition *cond; uid_t *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(uid_t)); if (data == NULL) { free(cond); return NULL; } *data = uid; cond->data = data; cond->free = cond_uid_free; cond->matches = cond_uid_matches; return cond; } /*************** find by timestamps condition *****************/ struct cond_times { time_t time; int what_time; /* 0 atime, 1 mtime, 2 ctime */ enum iso_find_comparisons comparison; }; static int cond_time_matches(IsoFindCondition *cond, IsoNode *node) { time_t node_time; struct cond_times *data = cond->data; switch (data->what_time) { case 0: node_time = node->atime; break; case 1: node_time = node->mtime; break; default: node_time = node->ctime; break; } switch (data->comparison) { case ISO_FIND_COND_GREATER: return node_time > data->time ? 1 : 0; case ISO_FIND_COND_GREATER_OR_EQUAL: return node_time >= data->time ? 1 : 0; case ISO_FIND_COND_EQUAL: return node_time == data->time ? 1 : 0; case ISO_FIND_COND_LESS: return node_time < data->time ? 1 : 0; case ISO_FIND_COND_LESS_OR_EQUAL: return node_time <= data->time ? 1 : 0; } /* should never happen */ return 0; } static void cond_time_free(IsoFindCondition *cond) { free(cond->data); } /** * Create a new condition that checks the time of last access. * * @param time * Time to compare against IsoNode atime. * @param comparison * Comparison to be done between IsoNode atime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_atime(time_t time, enum iso_find_comparisons comparison) { IsoFindCondition *cond; struct cond_times *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct cond_times)); if (data == NULL) { free(cond); return NULL; } data->time = time; data->comparison = comparison; data->what_time = 0; /* atime */ cond->data = data; cond->free = cond_time_free; cond->matches = cond_time_matches; return cond; } /** * Create a new condition that checks the time of last modification. * * @param time * Time to compare against IsoNode mtime. * @param comparison * Comparison to be done between IsoNode mtime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_mtime(time_t time, enum iso_find_comparisons comparison) { IsoFindCondition *cond; struct cond_times *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct cond_times)); if (data == NULL) { free(cond); return NULL; } data->time = time; data->comparison = comparison; data->what_time = 1; /* mtime */ cond->data = data; cond->free = cond_time_free; cond->matches = cond_time_matches; return cond; } /** * Create a new condition that checks the time of last status change. * * @param time * Time to compare against IsoNode ctime. * @param comparison * Comparison to be done between IsoNode ctime and submitted time. * Note that ISO_FIND_COND_GREATER, for example, is true if the node * time is greater than the submitted time. * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_ctime(time_t time, enum iso_find_comparisons comparison) { IsoFindCondition *cond; struct cond_times *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct cond_times)); if (data == NULL) { free(cond); return NULL; } data->time = time; data->comparison = comparison; data->what_time = 2; /* ctime */ cond->data = data; cond->free = cond_time_free; cond->matches = cond_time_matches; return cond; } /*************** logical operations on conditions *****************/ struct logical_binary_conditions { IsoFindCondition *a; IsoFindCondition *b; }; static void cond_logical_binary_free(IsoFindCondition *cond) { struct logical_binary_conditions *data; data = cond->data; data->a->free(data->a); free(data->a); data->b->free(data->b); free(data->b); free(cond->data); } static int cond_logical_and_matches(IsoFindCondition *cond, IsoNode *node) { struct logical_binary_conditions *data = cond->data; return data->a->matches(data->a, node) && data->b->matches(data->b, node); } /** * Create a new condition that check if the two given conditions are * valid. * * @param a * @param b * IsoFindCondition to compare * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_and(IsoFindCondition *a, IsoFindCondition *b) { IsoFindCondition *cond; struct logical_binary_conditions *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct logical_binary_conditions)); if (data == NULL) { free(cond); return NULL; } data->a = a; data->b = b; cond->data = data; cond->free = cond_logical_binary_free; cond->matches = cond_logical_and_matches; return cond; } static int cond_logical_or_matches(IsoFindCondition *cond, IsoNode *node) { struct logical_binary_conditions *data = cond->data; return data->a->matches(data->a, node) || data->b->matches(data->b, node); } /** * Create a new condition that check if at least one the two given conditions * is valid. * * @param a * @param b * IsoFindCondition to compare * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_or(IsoFindCondition *a, IsoFindCondition *b) { IsoFindCondition *cond; struct logical_binary_conditions *data; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } data = malloc(sizeof(struct logical_binary_conditions)); if (data == NULL) { free(cond); return NULL; } data->a = a; data->b = b; cond->data = data; cond->free = cond_logical_binary_free; cond->matches = cond_logical_or_matches; return cond; } static void cond_not_free(IsoFindCondition *cond) { IsoFindCondition *negate = cond->data; negate->free(negate); free(negate); } static int cond_not_matches(IsoFindCondition *cond, IsoNode *node) { IsoFindCondition *negate = cond->data; return !(negate->matches(negate, node)); } /** * Create a new condition that check if the given conditions is false. * * @param negate * @result * The created IsoFindCondition, NULL on error. * * @since 0.6.4 */ IsoFindCondition *iso_new_find_conditions_not(IsoFindCondition *negate) { IsoFindCondition *cond; cond = malloc(sizeof(IsoFindCondition)); if (cond == NULL) { return NULL; } cond->data = negate; cond->free = cond_not_free; cond->matches = cond_not_matches; return cond; }