/* Copyright (c) 2008-2012 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #include "xlator.h" #include #include #include #include "defaults.h" #define SET_DEFAULT_FOP(fn) do { \ if (!xl->fops->fn) \ xl->fops->fn = default_##fn; \ } while (0) #define SET_DEFAULT_CBK(fn) do { \ if (!xl->cbks->fn) \ xl->cbks->fn = default_##fn; \ } while (0) static void fill_defaults (xlator_t *xl) { if (xl == NULL) { gf_log_callingfn ("xlator", GF_LOG_WARNING, "invalid argument"); return; } SET_DEFAULT_FOP (create); SET_DEFAULT_FOP (open); SET_DEFAULT_FOP (stat); SET_DEFAULT_FOP (readlink); SET_DEFAULT_FOP (mknod); SET_DEFAULT_FOP (mkdir); SET_DEFAULT_FOP (unlink); SET_DEFAULT_FOP (rmdir); SET_DEFAULT_FOP (symlink); SET_DEFAULT_FOP (rename); SET_DEFAULT_FOP (link); SET_DEFAULT_FOP (truncate); SET_DEFAULT_FOP (readv); SET_DEFAULT_FOP (writev); SET_DEFAULT_FOP (statfs); SET_DEFAULT_FOP (flush); SET_DEFAULT_FOP (fsync); SET_DEFAULT_FOP (setxattr); SET_DEFAULT_FOP (getxattr); SET_DEFAULT_FOP (fsetxattr); SET_DEFAULT_FOP (fgetxattr); SET_DEFAULT_FOP (removexattr); SET_DEFAULT_FOP (fremovexattr); SET_DEFAULT_FOP (opendir); SET_DEFAULT_FOP (readdir); SET_DEFAULT_FOP (readdirp); SET_DEFAULT_FOP (fsyncdir); SET_DEFAULT_FOP (access); SET_DEFAULT_FOP (ftruncate); SET_DEFAULT_FOP (fstat); SET_DEFAULT_FOP (lk); SET_DEFAULT_FOP (inodelk); SET_DEFAULT_FOP (finodelk); SET_DEFAULT_FOP (entrylk); SET_DEFAULT_FOP (fentrylk); SET_DEFAULT_FOP (lookup); SET_DEFAULT_FOP (rchecksum); SET_DEFAULT_FOP (xattrop); SET_DEFAULT_FOP (fxattrop); SET_DEFAULT_FOP (setattr); SET_DEFAULT_FOP (fsetattr); SET_DEFAULT_FOP (fallocate); SET_DEFAULT_FOP (discard); SET_DEFAULT_FOP (zerofill); SET_DEFAULT_FOP (ipc); SET_DEFAULT_FOP (getspec); SET_DEFAULT_CBK (release); SET_DEFAULT_CBK (releasedir); SET_DEFAULT_CBK (forget); if (!xl->notify) xl->notify = default_notify; if (!xl->mem_acct_init) xl->mem_acct_init = default_mem_acct_init; return; } int xlator_set_type_virtual (xlator_t *xl, const char *type) { GF_VALIDATE_OR_GOTO ("xlator", xl, out); GF_VALIDATE_OR_GOTO ("xlator", type, out); xl->type = gf_strdup (type); if (xl->type) return 0; out: return -1; } int xlator_volopt_dynload (char *xlator_type, void **dl_handle, volume_opt_list_t *opt_list) { int ret = -1; char *name = NULL; void *handle = NULL; GF_VALIDATE_OR_GOTO ("xlator", xlator_type, out); ret = gf_asprintf (&name, "%s/%s.so", XLATORDIR, xlator_type); if (-1 == ret) { gf_log ("xlator", GF_LOG_ERROR, "asprintf failed"); goto out; } ret = -1; gf_log ("xlator", GF_LOG_TRACE, "attempt to load file %s", name); handle = dlopen (name, RTLD_NOW|RTLD_GLOBAL); if (!handle) { gf_log ("xlator", GF_LOG_WARNING, "%s", dlerror ()); goto out; } if (!(opt_list->given_opt = dlsym (handle, "options"))) { dlerror (); gf_log ("xlator", GF_LOG_ERROR, "Failed to load xlator opt table"); goto out; } *dl_handle = handle; handle = NULL; ret = 0; out: GF_FREE (name); if (handle) dlclose (handle); gf_log ("xlator", GF_LOG_DEBUG, "Returning %d", ret); return ret; } int xlator_dynload (xlator_t *xl) { int ret = -1; char *name = NULL; void *handle = NULL; volume_opt_list_t *vol_opt = NULL; class_methods_t *vtbl = NULL; GF_VALIDATE_OR_GOTO ("xlator", xl, out); INIT_LIST_HEAD (&xl->volume_options); ret = gf_asprintf (&name, "%s/%s.so", XLATORDIR, xl->type); if (-1 == ret) { gf_log ("xlator", GF_LOG_ERROR, "asprintf failed"); goto out; } ret = -1; gf_log ("xlator", GF_LOG_TRACE, "attempt to load file %s", name); handle = dlopen (name, RTLD_NOW|RTLD_GLOBAL); if (!handle) { gf_log ("xlator", GF_LOG_WARNING, "%s", dlerror ()); goto out; } xl->dlhandle = handle; if (!(xl->fops = dlsym (handle, "fops"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(fops) on %s", dlerror ()); goto out; } if (!(xl->cbks = dlsym (handle, "cbks"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(cbks) on %s", dlerror ()); goto out; } /* * If class_methods exists, its contents override any definitions of * init or fini for that translator. Otherwise, we fall back to the * older method of looking for init and fini directly. */ vtbl = dlsym(handle,"class_methods"); if (vtbl) { xl->init = vtbl->init; xl->fini = vtbl->fini; xl->reconfigure = vtbl->reconfigure; xl->notify = vtbl->notify; } else { if (!(*VOID(&xl->init) = dlsym (handle, "init"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(init) on %s", dlerror ()); goto out; } if (!(*VOID(&(xl->fini)) = dlsym (handle, "fini"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(fini) on %s", dlerror ()); goto out; } if (!(*VOID(&(xl->reconfigure)) = dlsym (handle, "reconfigure"))) { gf_log ("xlator", GF_LOG_TRACE, "dlsym(reconfigure) on %s -- neglecting", dlerror()); } if (!(*VOID(&(xl->notify)) = dlsym (handle, "notify"))) { gf_log ("xlator", GF_LOG_TRACE, "dlsym(notify) on %s -- neglecting", dlerror ()); } } if (!(xl->dumpops = dlsym (handle, "dumpops"))) { gf_log ("xlator", GF_LOG_TRACE, "dlsym(dumpops) on %s -- neglecting", dlerror ()); } if (!(*VOID(&(xl->mem_acct_init)) = dlsym (handle, "mem_acct_init"))) { gf_log (xl->name, GF_LOG_TRACE, "dlsym(mem_acct_init) on %s -- neglecting", dlerror ()); } vol_opt = GF_CALLOC (1, sizeof (volume_opt_list_t), gf_common_mt_volume_opt_list_t); if (!vol_opt) { goto out; } if (!(vol_opt->given_opt = dlsym (handle, "options"))) { dlerror (); gf_log (xl->name, GF_LOG_TRACE, "Strict option validation not enforced -- neglecting"); } INIT_LIST_HEAD (&vol_opt->list); list_add_tail (&vol_opt->list, &xl->volume_options); fill_defaults (xl); ret = 0; out: GF_FREE (name); return ret; } int xlator_set_type (xlator_t *xl, const char *type) { int ret = 0; ret = xlator_set_type_virtual (xl, type); if (!ret) ret = xlator_dynload (xl); return ret; } void xlator_set_inode_lru_limit (xlator_t *this, void *data) { int inode_lru_limit = 0; if (this->itable) { if (!data) { gf_log (this->name, GF_LOG_WARNING, "input data is " "NULL. Cannot update the lru limit of the inode" " table. Continuing with older value"); goto out; } inode_lru_limit = *(int *)data; inode_table_set_lru_limit (this->itable, inode_lru_limit); } out: return; } void xlator_foreach (xlator_t *this, void (*fn)(xlator_t *each, void *data), void *data) { xlator_t *first = NULL; xlator_t *old_THIS = NULL; GF_VALIDATE_OR_GOTO ("xlator", this, out); GF_VALIDATE_OR_GOTO ("xlator", fn, out); first = this; while (first->prev) first = first->prev; while (first) { old_THIS = THIS; THIS = first; fn (first, data); THIS = old_THIS; first = first->next; } out: return; } void xlator_foreach_depth_first (xlator_t *this, void (*fn)(xlator_t *each, void *data), void *data) { xlator_list_t *subv = NULL; subv = this->children; while (subv) { xlator_foreach_depth_first (subv->xlator, fn, data); subv = subv->next; } fn (this, data); } xlator_t * xlator_search_by_name (xlator_t *any, const char *name) { xlator_t *search = NULL; GF_VALIDATE_OR_GOTO ("xlator", any, out); GF_VALIDATE_OR_GOTO ("xlator", name, out); search = any; while (search->prev) search = search->prev; while (search) { if (!strcmp (search->name, name)) break; search = search->next; } out: return search; } static int __xlator_init(xlator_t *xl) { xlator_t *old_THIS = NULL; int ret = 0; old_THIS = THIS; THIS = xl; ret = xl->init (xl); THIS = old_THIS; return ret; } int xlator_init (xlator_t *xl) { int32_t ret = -1; GF_VALIDATE_OR_GOTO ("xlator", xl, out); if (xl->mem_acct_init) xl->mem_acct_init (xl); if (!xl->init) { gf_log (xl->name, GF_LOG_WARNING, "No init() found"); goto out; } ret = __xlator_init (xl); if (ret) { gf_log (xl->name, GF_LOG_ERROR, "Initialization of volume '%s' failed," " review your volfile again", xl->name); goto out; } xl->init_succeeded = 1; ret = 0; out: return ret; } static void xlator_fini_rec (xlator_t *xl) { xlator_list_t *trav = NULL; xlator_t *old_THIS = NULL; GF_VALIDATE_OR_GOTO ("xlator", xl, out); trav = xl->children; while (trav) { if (!trav->xlator->init_succeeded) { break; } xlator_fini_rec (trav->xlator); gf_log (trav->xlator->name, GF_LOG_DEBUG, "fini done"); trav = trav->next; } if (xl->init_succeeded) { if (xl->fini) { old_THIS = THIS; THIS = xl; xl->fini (xl); if (xl->local_pool) mem_pool_destroy (xl->local_pool); THIS = old_THIS; } else { gf_log (xl->name, GF_LOG_DEBUG, "No fini() found"); } xl->init_succeeded = 0; } out: return; } int xlator_notify (xlator_t *xl, int event, void *data, ...) { xlator_t *old_THIS = NULL; int ret = 0; old_THIS = THIS; THIS = xl; ret = xl->notify (xl, event, data); THIS = old_THIS; return ret; } int xlator_mem_acct_init (xlator_t *xl, int num_types) { int i = 0; int ret = 0; if (!xl) return -1; if (!xl->ctx) return -1; if (!xl->ctx->mem_acct_enable) return 0; xl->mem_acct = MALLOC (sizeof(struct mem_acct) + sizeof(struct mem_acct_rec) * num_types); if (!xl->mem_acct) { return -1; } memset (xl->mem_acct, 0, sizeof(struct mem_acct)); xl->mem_acct->num_types = num_types; LOCK_INIT (&xl->mem_acct->lock); xl->mem_acct->refcnt = 1; for (i = 0; i < num_types; i++) { memset (&xl->mem_acct->rec[i], 0, sizeof(struct mem_acct_rec)); ret = LOCK_INIT(&(xl->mem_acct->rec[i].lock)); if (ret) { fprintf(stderr, "Unable to lock..errno : %d",errno); } } return 0; } void xlator_tree_fini (xlator_t *xl) { xlator_t *top = NULL; GF_VALIDATE_OR_GOTO ("xlator", xl, out); top = xl; xlator_fini_rec (top); out: return; } int xlator_list_destroy (xlator_list_t *list) { xlator_list_t *next = NULL; while (list) { next = list->next; GF_FREE (list); list = next; } return 0; } static int xlator_memrec_free (xlator_t *xl) { uint32_t i = 0; struct mem_acct *mem_acct = NULL; if (!xl) { return 0; } mem_acct = xl->mem_acct; if (mem_acct) { for (i = 0; i < mem_acct->num_types; i++) { LOCK_DESTROY (&(mem_acct->rec[i].lock)); } if (DECREMENT_ATOMIC (mem_acct->lock, mem_acct->refcnt) == 0) { FREE (mem_acct); xl->mem_acct = NULL; } } return 0; } static int xlator_members_free (xlator_t *xl) { volume_opt_list_t *vol_opt = NULL; volume_opt_list_t *tmp = NULL; if (!xl) return 0; GF_FREE (xl->name); GF_FREE (xl->type); if (xl->dlhandle) dlclose (xl->dlhandle); if (xl->options) dict_unref (xl->options); xlator_list_destroy (xl->children); xlator_list_destroy (xl->parents); list_for_each_entry_safe (vol_opt, tmp, &xl->volume_options, list) { list_del_init (&vol_opt->list); GF_FREE (vol_opt); } return 0; } /* This function destroys all the xlator members except for the * xlator strcuture and its mem accounting field. * * If otherwise, it would destroy the master xlator object as well * its mem accounting, which would mean after calling glusterfs_graph_destroy() * there cannot be any reference to GF_FREE() from the master xlator, this is * not possible because of the following dependencies: * - glusterfs_ctx_t will have mem pools allocated by the master xlators * - xlator objects will have references to those mem pools(g: dict) * * Ordering the freeing in any of the order will also not solve the dependency: * - Freeing xlator objects(including memory accounting) before mem pools * destruction will mean not use GF_FREE while destroying mem pools. * - Freeing mem pools and then destroying xlator objects would lead to crashes * when xlator tries to unref dict or other mem pool objects. * * Hence the way chosen out of this interdependency is to split xlator object * free into two stages: * - Free all the xlator members excpet for its mem accounting structure * - Free all the mem accouting structures of xlator along with the xlator * object itself. * * This two stages of destruction, is mainly required for glfs_fini(). */ int xlator_tree_free_members (xlator_t *tree) { xlator_t *trav = tree; xlator_t *prev = tree; if (!tree) { gf_log ("parser", GF_LOG_ERROR, "Translator tree not found"); return -1; } while (prev) { trav = prev->next; xlator_members_free (prev); prev = trav; } return 0; } int xlator_tree_free_memacct (xlator_t *tree) { xlator_t *trav = tree; xlator_t *prev = tree; if (!tree) { gf_log ("parser", GF_LOG_ERROR, "Translator tree not found"); return -1; } while (prev) { trav = prev->next; xlator_memrec_free (prev); prev = trav; } return 0; } void loc_wipe (loc_t *loc) { if (loc->inode) { inode_unref (loc->inode); loc->inode = NULL; } if (loc->path) { GF_FREE ((char *)loc->path); loc->path = NULL; } if (loc->parent) { inode_unref (loc->parent); loc->parent = NULL; } memset (loc, 0, sizeof (*loc)); } int loc_path (loc_t *loc, const char *bname) { int ret = 0; if (loc->path) goto out; ret = -1; if (bname && !strlen (bname)) bname = NULL; if (!bname) goto inode_path; if (loc->parent && !gf_uuid_is_null (loc->parent->gfid)) { ret = inode_path (loc->parent, bname, (char**)&loc->path); } else if (!gf_uuid_is_null (loc->pargfid)) { ret = gf_asprintf ((char**)&loc->path, INODE_PATH_FMT"/%s", uuid_utoa (loc->pargfid), bname); } if (loc->path) goto out; inode_path: if (loc->inode && !gf_uuid_is_null (loc->inode->gfid)) { ret = inode_path (loc->inode, NULL, (char **)&loc->path); } else if (!gf_uuid_is_null (loc->gfid)) { ret = gf_asprintf ((char**)&loc->path, INODE_PATH_FMT, uuid_utoa (loc->gfid)); } out: return ret; } void loc_gfid (loc_t *loc, uuid_t gfid) { if (!gfid) goto out; gf_uuid_clear (gfid); if (!loc) goto out; else if (!gf_uuid_is_null (loc->gfid)) gf_uuid_copy (gfid, loc->gfid); else if (loc->inode && (!gf_uuid_is_null (loc->inode->gfid))) gf_uuid_copy (gfid, loc->inode->gfid); out: return; } char* loc_gfid_utoa (loc_t *loc) { uuid_t gfid = {0, }; loc_gfid (loc, gfid); return uuid_utoa (gfid); } int loc_touchup (loc_t *loc, const char *name) { char *path = NULL; int ret = 0; if (loc->path) goto out; if (loc->parent && name && strlen (name)) { ret = inode_path (loc->parent, name, &path); if (path) /*Guaranteed to have trailing '/' */ loc->name = strrchr (path, '/') + 1; if (gf_uuid_is_null (loc->pargfid)) gf_uuid_copy (loc->pargfid, loc->parent->gfid); } else if (loc->inode) { ret = inode_path (loc->inode, 0, &path); if (gf_uuid_is_null (loc->gfid)) gf_uuid_copy (loc->gfid, loc->inode->gfid); } if (ret < 0 || !path) { ret = -ENOMEM; goto out; } loc->path = path; ret = 0; out: return ret; } int loc_copy_overload_parent (loc_t *dst, loc_t *src, inode_t *parent) { int ret = -1; GF_VALIDATE_OR_GOTO ("xlator", dst, err); GF_VALIDATE_OR_GOTO ("xlator", src, err); GF_VALIDATE_OR_GOTO ("xlator", parent, err); gf_uuid_copy (dst->gfid, src->gfid); gf_uuid_copy (dst->pargfid, parent->gfid); if (src->inode) dst->inode = inode_ref (src->inode); if (parent) dst->parent = inode_ref (parent); if (src->path) { dst->path = gf_strdup (src->path); if (!dst->path) goto out; if (src->name) dst->name = strrchr (dst->path, '/'); if (dst->name) dst->name++; } else if (src->name) { dst->name = src->name; } ret = 0; out: if (ret == -1) loc_wipe (dst); err: return ret; } int loc_copy (loc_t *dst, loc_t *src) { int ret = -1; GF_VALIDATE_OR_GOTO ("xlator", dst, err); GF_VALIDATE_OR_GOTO ("xlator", src, err); gf_uuid_copy (dst->gfid, src->gfid); gf_uuid_copy (dst->pargfid, src->pargfid); if (src->inode) dst->inode = inode_ref (src->inode); if (src->parent) dst->parent = inode_ref (src->parent); if (src->path) { dst->path = gf_strdup (src->path); if (!dst->path) goto out; if (src->name) dst->name = strrchr (dst->path, '/'); if (dst->name) dst->name++; } else if (src->name) { dst->name = src->name; } ret = 0; out: if (ret == -1) loc_wipe (dst); err: return ret; } gf_boolean_t loc_is_root (loc_t *loc) { if (loc && __is_root_gfid (loc->gfid)) { return _gf_true; } else if (loc && loc->inode && __is_root_gfid (loc->inode->gfid)) { return _gf_true; } return _gf_false; } int32_t loc_build_child (loc_t *child, loc_t *parent, char *name) { int32_t ret = -1; GF_VALIDATE_OR_GOTO ("xlator", child, out); GF_VALIDATE_OR_GOTO ("xlator", parent, out); GF_VALIDATE_OR_GOTO ("xlator", name, out); loc_gfid (parent, child->pargfid); if (strcmp (parent->path, "/") == 0) ret = gf_asprintf ((char **)&child->path, "/%s", name); else ret = gf_asprintf ((char **)&child->path, "%s/%s", parent->path, name); if (ret < 0 || !child->path) { ret = -1; goto out; } child->name = strrchr (child->path, '/') + 1; child->parent = inode_ref (parent->inode); child->inode = inode_new (parent->inode->table); if (!child->inode) { ret = -1; goto out; } ret = 0; out: if ((ret < 0) && child) loc_wipe (child); return ret; } int xlator_destroy (xlator_t *xl) { if (!xl) return 0; xlator_members_free (xl); xlator_memrec_free (xl); GF_FREE (xl); return 0; } int is_gf_log_command (xlator_t *this, const char *name, char *value) { xlator_t *trav = NULL; char key[1024] = {0,}; int ret = -1; int log_level = -1; gf_boolean_t syslog_flag = 0; glusterfs_ctx_t *ctx = NULL; if (!strcmp ("trusted.glusterfs.syslog", name)) { ret = gf_string2boolean (value, &syslog_flag); if (ret) { ret = EOPNOTSUPP; goto out; } if (syslog_flag) gf_log_enable_syslog (); else gf_log_disable_syslog (); goto out; } if (fnmatch ("trusted.glusterfs*set-log-level", name, FNM_NOESCAPE)) goto out; log_level = glusterd_check_log_level (value); if (log_level == -1) { ret = EOPNOTSUPP; goto out; } /* Some crude way to change the log-level of process */ if (!strcmp (name, "trusted.glusterfs.set-log-level")) { /* */ gf_log ("glusterfs", gf_log_get_loglevel(), "setting log level to %d (old-value=%d)", log_level, gf_log_get_loglevel()); gf_log_set_loglevel (log_level); ret = 0; goto out; } if (!strcmp (name, "trusted.glusterfs.fuse.set-log-level")) { /* */ gf_log (this->name, gf_log_get_xl_loglevel (this), "setting log level to %d (old-value=%d)", log_level, gf_log_get_xl_loglevel (this)); gf_log_set_xl_loglevel (this, log_level); ret = 0; goto out; } ctx = this->ctx; if (!ctx) goto out; if (!ctx->active) goto out; trav = ctx->active->top; while (trav) { snprintf (key, 1024, "trusted.glusterfs.%s.set-log-level", trav->name); if (fnmatch (name, key, FNM_NOESCAPE) == 0) { gf_log (trav->name, gf_log_get_xl_loglevel (trav), "setting log level to %d (old-value=%d)", log_level, gf_log_get_xl_loglevel (trav)); gf_log_set_xl_loglevel (trav, log_level); ret = 0; } trav = trav->next; } out: return ret; } int glusterd_check_log_level (const char *value) { int log_level = -1; if (!strcasecmp (value, "CRITICAL")) { log_level = GF_LOG_CRITICAL; } else if (!strcasecmp (value, "ERROR")) { log_level = GF_LOG_ERROR; } else if (!strcasecmp (value, "WARNING")) { log_level = GF_LOG_WARNING; } else if (!strcasecmp (value, "INFO")) { log_level = GF_LOG_INFO; } else if (!strcasecmp (value, "DEBUG")) { log_level = GF_LOG_DEBUG; } else if (!strcasecmp (value, "TRACE")) { log_level = GF_LOG_TRACE; } else if (!strcasecmp (value, "NONE")) { log_level = GF_LOG_NONE; } if (log_level == -1) gf_log (THIS->name, GF_LOG_ERROR, "Invalid log-level. possible values " "are DEBUG|WARNING|ERROR|CRITICAL|NONE|TRACE"); return log_level; } int xlator_subvolume_count (xlator_t *this) { int i = 0; xlator_list_t *list = NULL; for (list = this->children; list; list = list->next) i++; return i; }