diff options
Diffstat (limited to 'libglusterfs/src/common-utils.c')
| -rw-r--r-- | libglusterfs/src/common-utils.c | 6119 |
1 files changed, 4848 insertions, 1271 deletions
diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c index b9f752b750b..682cbf28055 100644 --- a/libglusterfs/src/common-utils.c +++ b/libglusterfs/src/common-utils.c @@ -1,29 +1,17 @@ /* - Copyright (c) 2006-2011 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> This file is part of GlusterFS. - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. + 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 - #ifdef HAVE_BACKTRACE #include <execinfo.h> +#else +#include "execinfo_compat.h" #endif #include <stdio.h> @@ -36,1853 +24,5442 @@ #include <time.h> #include <locale.h> #include <sys/socket.h> -#include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> +#include <assert.h> +#include <libgen.h> /* for dirname() */ +#include <grp.h> -#include "logging.h" -#include "common-utils.h" -#include "revision.h" -#include "glusterfs.h" -#include "stack.h" -#include "globals.h" -#include "md5.h" +#if defined(GF_BSD_HOST_OS) || defined(GF_DARWIN_HOST_OS) +#include <sys/sysctl.h> +#endif +#ifndef GF_LINUX_HOST_OS +#include <sys/resource.h> +#endif +#ifdef HAVE_SYNCFS_SYS +#include <sys/syscall.h> +#endif + +#include "glusterfs/compat-errno.h" +#include "glusterfs/common-utils.h" +#include "glusterfs/revision.h" +#include "glusterfs/glusterfs.h" +#include "glusterfs/stack.h" +#include "glusterfs/lkowner.h" +#include "glusterfs/syscall.h" +#include "glusterfs/globals.h" +#define XXH_INLINE_ALL +#include "xxhash.h" +#include <ifaddrs.h> +#include "glusterfs/libglusterfs-messages.h" +#include "glusterfs/glusterfs-acl.h" +#ifdef __FreeBSD__ +#include <pthread_np.h> +#undef BIT_SET +#endif #ifndef AI_ADDRCONFIG #define AI_ADDRCONFIG 0 #endif /* AI_ADDRCONFIG */ +char *vol_type_str[] = { + "Distribute", + "Stripe [NOT SUPPORTED from v6.0]", + "Replicate", + "Striped-Replicate [NOT SUPPORTED from v6.0]", + "Disperse", + "Tier [NOT SUPPORTED from v6.0]", + "Distributed-Stripe [NOT SUPPORTED from v6.0]", + "Distributed-Replicate", + "Distributed-Striped-Replicate [NOT SUPPORTED from v6.0]", + "Distributed-Disperse", +}; + typedef int32_t (*rw_op_t)(int32_t fd, char *buf, int32_t size); typedef int32_t (*rwv_op_t)(int32_t fd, const struct iovec *buf, int32_t size); -struct dnscache6 { - struct addrinfo *first; - struct addrinfo *next; -}; +char *xattrs_to_heal[] = {"user.", + POSIX_ACL_ACCESS_XATTR, + POSIX_ACL_DEFAULT_XATTR, + QUOTA_LIMIT_KEY, + QUOTA_LIMIT_OBJECTS_KEY, + GF_SELINUX_XATTR_KEY, + GF_XATTR_MDATA_KEY, + NULL}; +void +gf_xxh64_wrapper(const unsigned char *data, size_t const len, + unsigned long long const seed, char *xxh64) +{ + unsigned short i = 0; + const unsigned short lim = GF_XXH64_DIGEST_LENGTH * 2 + 1; + XXH64_hash_t hash = 0; + XXH64_canonical_t c_hash = { + { + 0, + }, + }; + const uint8_t *p = (const uint8_t *)&c_hash; + + hash = XXH64(data, len, seed); + XXH64_canonicalFromHash(&c_hash, hash); + + for (i = 0; i < GF_XXH64_DIGEST_LENGTH; i++) + snprintf(xxh64 + i * 2, lim - i * 2, "%02x", p[i]); +} +/** + * This function takes following arguments + * @this: xlator + * @gfid: The gfid which has to be filled + * @hash: the 8 byte hash which has to be filled inside the gfid + * @index: the array element of the uuid_t structure (which is + * a array of unsigned char) from where the 8 bytes of + * the hash has to be filled. Since uuid_t contains 16 + * char elements in the array, each byte of the hash has + * to be filled in one array element. + * + * This function is called twice for 2 hashes (of 8 byte each) to + * be filled in the gfid. + * + * The for loop in this function actually is doing these 2 things + * for each hash + * + * 1) One of the hashes + * tmp[0] = (hash_2 >> 56) & 0xff; + * tmp[1] = (hash_2 >> 48) & 0xff; + * tmp[2] = (hash_2 >> 40) & 0xff; + * tmp[3] = (hash_2 >> 32) & 0xff; + * tmp[4] = (hash_2 >> 24) & 0xff; + * tmp[5] = (hash_2 >> 16) & 0xff; + * tmp[6] = (hash_2 >> 8) & 0xff; + * tmp[7] = (hash_2) & 0xff; + * + * 2) The other hash: + * tmp[8] = (hash_1 >> 56) & 0xff; + * tmp[9] = (hash_1 >> 48) & 0xff; + * tmp[10] = (hash_1 >> 40) & 0xff; + * tmp[11] = (hash_1 >> 32) & 0xff; + * tmp[12] = (hash_1 >> 24) & 0xff; + * tmp[13] = (hash_1 >> 16) & 0xff; + * tmp[14] = (hash_1 >> 8) & 0xff; + * tmp[15] = (hash_1) & 0xff; + **/ +static int +gf_gfid_from_xxh64(xlator_t *this, uuid_t gfid, XXH64_hash_t hash, + unsigned short index) +{ + int ret = -1; + int i = -1; + + if ((index != 0) && (index != 8)) { + gf_msg_callingfn("gfid-from-xxh64", GF_LOG_WARNING, 0, + LG_MSG_INDEX_NOT_FOUND, + "index can only be either 0 or 8, as this" + "function's purpose is to encode a 8 byte " + "hash inside the gfid (index: %d)", + index); + goto out; + } + + for (i = 0; i < sizeof(hash); i++) { + /* + * As of now the below statement is equivalent of this. + * gfid[index+i] = (hash >> (64 - (8 * (i+1)))) & 0xff; + */ + gfid[index + i] = (hash >> ((sizeof(hash) * 8) - (8 * (i + 1)))) & + (0xff); + } + + ret = 0; + +out: + return ret; +} + +/** + * This function does the same thing as gf_xxh64_wrapper. But gf_xxh64_wrapper + * does not return anything and in this xlator there is a need for both the + * actual hash and the canonicalized form of the hash. + * + * To summarize: + * - XXH64_hash_t is needed as return because, those bytes which contain the + * hash can be used for different purposes as needed. One example is + * to have those bytes copied into the uuid_t structure to be used as gfid + * - xxh64 string is needed because, it can be used as the key for generating + * the next hash (and any other purpose which might require canonical form + * of the hash). + **/ +XXH64_hash_t +gf_xxh64_hash_wrapper(const unsigned char *data, size_t const len, + unsigned long long const seed, char *xxh64) +{ + unsigned short i = 0; + const unsigned short lim = GF_XXH64_DIGEST_LENGTH * 2 + 1; + XXH64_hash_t hash = 0; + XXH64_canonical_t c_hash = { + { + 0, + }, + }; + const uint8_t *p = (const uint8_t *)&c_hash; + + hash = XXH64(data, len, seed); + XXH64_canonicalFromHash(&c_hash, hash); + + for (i = 0; i < GF_XXH64_DIGEST_LENGTH; i++) + snprintf(xxh64 + i * 2, lim - i * 2, "%02x", p[i]); + + return hash; +} + +/** + * This is the algorithm followed for generating new gfid + * 1) generate xxh64 hash using snapname and original gfid of the object + * 2) Using the canonicalized form of above hash as the key, generate + * another hash + * 3) Combine both of the 8 byte hashes to generate a 16 byte uuid_t type + * 4) Use the above uuid as the gfid + * + * Each byte of the hash is stored separately in different elements of the + * character array represented by uuid_t + * Ex: tmp[0] = (hash_2 >> 56) & 0xFF + * This saves the most significant byte of hash_2 in tmp[0] + * tmp[1] = (hash_2 >> 48) & 0xFF + * This saves next most significant byte of hash_2 in tmp[1] + * . + * . + * So on. + * tmp[0] - tmp[7] holds the contents of hash_2 + * tmp[8] - tmp[15] hold the conents of hash_1 + * + * The hash generated (i.e. of type XXH64_hash_t) is 8 bytes long. And for + * gfid 16 byte uuid is needed. Hecne the 2 hashes are combined to form + * one 16 byte entity. + **/ int -log_base2 (unsigned long x) +gf_gfid_generate_from_xxh64(uuid_t gfid, char *key) { - int val = 0; + char xxh64_1[GF_XXH64_DIGEST_LENGTH * 2 + 1] = { + 0, + }; + char xxh64_2[GF_XXH64_DIGEST_LENGTH * 2 + 1] = { + 0, + }; + XXH64_hash_t hash_1 = 0; + XXH64_hash_t hash_2 = 0; + int ret = -1; + xlator_t *this = THIS; + + hash_1 = gf_xxh64_hash_wrapper((unsigned char *)key, strlen(key), + GF_XXHSUM64_DEFAULT_SEED, xxh64_1); + + hash_2 = gf_xxh64_hash_wrapper((unsigned char *)xxh64_1, strlen(xxh64_1), + GF_XXHSUM64_DEFAULT_SEED, xxh64_2); + + /* hash_2 is saved in 1st 8 elements of uuid_t char array */ + if (gf_gfid_from_xxh64(this, gfid, hash_2, 0)) { + gf_msg_callingfn(this->name, GF_LOG_WARNING, 0, + LG_MSG_XXH64_TO_GFID_FAILED, + "failed to encode the hash %llx into the 1st" + "half of gfid", + hash_2); + goto out; + } + + /* hash_1 is saved in the remaining 8 elements of uuid_t */ + if (gf_gfid_from_xxh64(this, gfid, hash_1, 8)) { + gf_msg_callingfn(this->name, GF_LOG_WARNING, 0, + LG_MSG_XXH64_TO_GFID_FAILED, + "failed to encode the hash %llx into the 2nd" + "half of gfid", + hash_1); + goto out; + } + + gf_msg_debug(this->name, 0, + "gfid generated is %s (hash1: %llx) " + "hash2: %llx, xxh64_1: %s xxh64_2: %s", + uuid_utoa(gfid), hash_1, hash_2, xxh64_1, xxh64_2); + + ret = 0; - while (x > 1) { - x /= 2; - val++; +out: + return ret; +} + +/* works similar to mkdir(1) -p. + */ +int +mkdir_p(char *path, mode_t mode, gf_boolean_t allow_symlinks) +{ + int i = 0; + int ret = -1; + char dir[PATH_MAX] = { + 0, + }; + struct stat stbuf = { + 0, + }; + + const int path_len = min(strlen(path), PATH_MAX - 1); + + snprintf(dir, path_len + 1, "%s", path); + + i = (dir[0] == '/') ? 1 : 0; + do { + if (path[i] != '/' && path[i] != '\0') + continue; + + dir[i] = '\0'; + ret = sys_mkdir(dir, mode); + if (ret && errno != EEXIST) { + gf_smsg("", GF_LOG_ERROR, errno, LG_MSG_DIR_OP_FAILED, NULL); + goto out; } - return val; + if (ret && errno == EEXIST && !allow_symlinks) { + ret = sys_lstat(dir, &stbuf); + if (ret) + goto out; + + if (S_ISLNK(stbuf.st_mode)) { + ret = -1; + gf_smsg("", GF_LOG_ERROR, 0, LG_MSG_DIR_IS_SYMLINK, "dir=%s", + dir, NULL); + goto out; + } + } + dir[i] = '/'; + + } while (path[i++] != '\0'); + + ret = sys_stat(dir, &stbuf); + if (ret || !S_ISDIR(stbuf.st_mode)) { + if (ret == 0) + errno = 0; + ret = -1; + gf_smsg("", GF_LOG_ERROR, errno, LG_MSG_DIR_OP_FAILED, + "possibly some of the components" + " were not directories", + NULL); + goto out; + } + + ret = 0; +out: + + return ret; +} + +int +gf_lstat_dir(const char *path, struct stat *stbuf_in) +{ + int ret = -1; + struct stat stbuf = { + 0, + }; + + if (path == NULL) { + errno = EINVAL; + goto out; + } + + ret = sys_lstat(path, &stbuf); + if (ret) + goto out; + + if (!S_ISDIR(stbuf.st_mode)) { + errno = ENOTDIR; + ret = -1; + goto out; + } + ret = 0; + +out: + if (!ret && stbuf_in) + *stbuf_in = stbuf; + + return ret; +} + +int +log_base2(unsigned long x) +{ + int val = 0; + + while (x > 1) { + x /= 2; + val++; + } + + return val; } +/** + * gf_rev_dns_lookup -- Perform a reverse DNS lookup on the IP address. + * + * @ip: The IP address to perform a reverse lookup on + * + * @return: success: Allocated string containing the hostname + * failure: NULL + */ +char * +gf_rev_dns_lookup(const char *ip) +{ + char *fqdn = NULL; + int ret = 0; + + GF_VALIDATE_OR_GOTO("resolver", ip, out); + + /* Get the FQDN */ + ret = gf_get_hostname_from_ip((char *)ip, &fqdn); + if (ret != 0) { + gf_smsg("resolver", GF_LOG_INFO, errno, LG_MSG_RESOLVE_HOSTNAME_FAILED, + "hostname=%s", ip, NULL); + } +out: + return fqdn; +} + +/** + * gf_resolve_path_parent -- Given a path, returns an allocated string + * containing the parent's path. + * @path: Path to parse + * @return: The parent path if found, NULL otherwise + */ +char * +gf_resolve_path_parent(const char *path) +{ + char *parent = NULL; + char *tmp = NULL; + char *pathc = NULL; + + GF_VALIDATE_OR_GOTO(THIS->name, path, out); + + if (0 == strlen(path)) { + gf_msg_callingfn(THIS->name, GF_LOG_DEBUG, 0, LG_MSG_INVALID_STRING, + "invalid string for 'path'"); + goto out; + } + + /* dup the parameter, we don't want to modify it */ + pathc = strdupa(path); + if (!pathc) { + goto out; + } + + /* Get the parent directory */ + tmp = dirname(pathc); + if (strcmp(tmp, "/") == 0) + goto out; + + parent = gf_strdup(tmp); +out: + return parent; +} int32_t -gf_resolve_ip6 (const char *hostname, - uint16_t port, - int family, - void **dnscache, - struct addrinfo **addr_info) -{ - int32_t ret = 0; - struct addrinfo hints; - struct dnscache6 *cache = NULL; - char service[NI_MAXSERV], host[NI_MAXHOST]; - - if (!hostname) { - gf_log_callingfn ("resolver", GF_LOG_WARNING, "hostname is NULL"); - return -1; +gf_resolve_ip6(const char *hostname, uint16_t port, int family, void **dnscache, + struct addrinfo **addr_info) +{ + int32_t ret = 0; + struct addrinfo hints; + struct dnscache6 *cache = NULL; + char service[NI_MAXSERV], host[NI_MAXHOST]; + + if (!hostname) { + gf_msg_callingfn("resolver", GF_LOG_WARNING, 0, LG_MSG_HOSTNAME_NULL, + "hostname is NULL"); + return -1; + } + + if (!*dnscache) { + *dnscache = GF_CALLOC(1, sizeof(struct dnscache6), + gf_common_mt_dnscache6); + if (!*dnscache) + return -1; + } + + cache = *dnscache; + if (cache->first && !cache->next) { + freeaddrinfo(cache->first); + cache->first = cache->next = NULL; + gf_msg_trace("resolver", 0, "flushing DNS cache"); + } + + if (!cache->first) { + char *port_str = NULL; + gf_msg_trace("resolver", 0, + "DNS cache not present, freshly " + "probing hostname: %s", + hostname); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + + ret = gf_asprintf(&port_str, "%d", port); + if (-1 == ret) { + return -1; } - - if (!*dnscache) { - *dnscache = GF_CALLOC (1, sizeof (struct dnscache6), - gf_common_mt_dnscache6); - if (!*dnscache) - return -1; + if ((ret = getaddrinfo(hostname, port_str, &hints, &cache->first)) != + 0) { + gf_smsg("resolver", GF_LOG_ERROR, 0, LG_MSG_GETADDRINFO_FAILED, + "family=%d", family, "ret=%s", gai_strerror(ret), NULL); + + GF_FREE(*dnscache); + *dnscache = NULL; + GF_FREE(port_str); + return -1; } - - cache = *dnscache; - if (cache->first && !cache->next) { - freeaddrinfo(cache->first); - cache->first = cache->next = NULL; - gf_log ("resolver", GF_LOG_TRACE, - "flushing DNS cache"); + GF_FREE(port_str); + + cache->next = cache->first; + } + + if (cache->next) { + ret = getnameinfo((struct sockaddr *)cache->next->ai_addr, + cache->next->ai_addrlen, host, sizeof(host), service, + sizeof(service), NI_NUMERICHOST); + if (ret != 0) { + gf_smsg("resolver", GF_LOG_ERROR, 0, LG_MSG_GETNAMEINFO_FAILED, + "ret=%s", gai_strerror(ret), NULL); + goto err; } - if (!cache->first) { - char *port_str = NULL; - gf_log ("resolver", GF_LOG_TRACE, - "DNS cache not present, freshly probing hostname: %s", - hostname); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - - ret = gf_asprintf (&port_str, "%d", port); - if (-1 == ret) { - gf_log ("resolver", GF_LOG_ERROR, "asprintf failed"); - return -1; - } - if ((ret = getaddrinfo(hostname, port_str, &hints, &cache->first)) != 0) { - gf_log ("resolver", GF_LOG_ERROR, - "getaddrinfo failed (%s)", gai_strerror (ret)); - - GF_FREE (*dnscache); - *dnscache = NULL; - GF_FREE (port_str); - return -1; - } - GF_FREE (port_str); - - cache->next = cache->first; + gf_msg_debug("resolver", 0, + "returning ip-%s (port-%s) for " + "hostname: %s and port: %d", + host, service, hostname, port); + + *addr_info = cache->next; + } + + if (cache->next) + cache->next = cache->next->ai_next; + if (cache->next) { + ret = getnameinfo((struct sockaddr *)cache->next->ai_addr, + cache->next->ai_addrlen, host, sizeof(host), service, + sizeof(service), NI_NUMERICHOST); + if (ret != 0) { + gf_smsg("resolver", GF_LOG_ERROR, 0, LG_MSG_GETNAMEINFO_FAILED, + "ret=%s", gai_strerror(ret), NULL); + goto err; } - if (cache->next) { - ret = getnameinfo((struct sockaddr *)cache->next->ai_addr, - cache->next->ai_addrlen, - host, sizeof (host), - service, sizeof (service), - NI_NUMERICHOST); - if (ret != 0) { - gf_log ("resolver", GF_LOG_ERROR, - "getnameinfo failed (%s)", gai_strerror (ret)); - goto err; - } - - gf_log ("resolver", GF_LOG_DEBUG, - "returning ip-%s (port-%s) for hostname: %s and port: %d", - host, service, hostname, port); - - *addr_info = cache->next; + gf_msg_debug("resolver", 0, + "next DNS query will return: " + "ip-%s port-%s", + host, service); + } + + return 0; + +err: + freeaddrinfo(cache->first); + cache->first = cache->next = NULL; + GF_FREE(cache); + *dnscache = NULL; + return -1; +} + +/** + * gf_dnscache_init -- Initializes a dnscache struct and sets the ttl + * to the specified value in the parameter. + * + * @ttl: the TTL in seconds + * @return: SUCCESS: Pointer to an allocated dnscache struct + * FAILURE: NULL + */ +struct dnscache * +gf_dnscache_init(time_t ttl) +{ + struct dnscache *cache = GF_MALLOC(sizeof(*cache), gf_common_mt_dnscache); + if (!cache) + return NULL; + + cache->cache_dict = dict_new(); + if (!cache->cache_dict) { + GF_FREE(cache); + cache = NULL; + } else { + cache->ttl = ttl; + } + + return cache; +} + +/** + * gf_dnscache_deinit -- cleanup resources used by struct dnscache + */ +void +gf_dnscache_deinit(struct dnscache *cache) +{ + if (!cache) { + gf_msg_plain(GF_LOG_WARNING, "dnscache is NULL"); + return; + } + dict_unref(cache->cache_dict); + GF_FREE(cache); +} + +/** + * gf_dnscache_entry_init -- Initialize a dnscache entry + * + * @return: SUCCESS: Pointer to an allocated dnscache entry struct + * FAILURE: NULL + */ +struct dnscache_entry * +gf_dnscache_entry_init() +{ + struct dnscache_entry *entry = GF_CALLOC(1, sizeof(*entry), + gf_common_mt_dnscache_entry); + return entry; +} + +/** + * gf_dnscache_entry_deinit -- Free memory used by a dnscache entry + * + * @entry: Pointer to deallocate + */ +void +gf_dnscache_entry_deinit(struct dnscache_entry *entry) +{ + GF_FREE(entry->ip); + GF_FREE(entry->fqdn); + GF_FREE(entry); +} + +/** + * gf_rev_dns_lookup -- Perform a reverse DNS lookup on the IP address. + * + * @ip: The IP address to perform a reverse lookup on + * + * @return: success: Allocated string containing the hostname + * failure: NULL + */ +char * +gf_rev_dns_lookup_cached(const char *ip, struct dnscache *dnscache) +{ + char *fqdn = NULL; + int ret = 0; + dict_t *cache = NULL; + data_t *entrydata = NULL; + struct dnscache_entry *dnsentry = NULL; + gf_boolean_t from_cache = _gf_false; + + if (!dnscache) + goto out; + + cache = dnscache->cache_dict; + + /* Quick cache lookup to see if we already hold it */ + entrydata = dict_get(cache, (char *)ip); + if (entrydata) { + dnsentry = (struct dnscache_entry *)entrydata->data; + /* First check the TTL & timestamp */ + if (gf_time() - dnsentry->timestamp > dnscache->ttl) { + gf_dnscache_entry_deinit(dnsentry); + entrydata->data = NULL; /* Mark this as 'null' so + * dict_del () doesn't try free + * this after we've already + * freed it. + */ + + dict_del(cache, (char *)ip); /* Remove this entry */ + } else { + /* Cache entry is valid, get the FQDN and return */ + fqdn = dnsentry->fqdn; + from_cache = _gf_true; /* Mark this as from cache */ + goto out; } + } + + /* Get the FQDN */ + ret = gf_get_hostname_from_ip((char *)ip, &fqdn); + if (ret != 0) + goto out; - if (cache->next) - cache->next = cache->next->ai_next; - if (cache->next) { - ret = getnameinfo((struct sockaddr *)cache->next->ai_addr, - cache->next->ai_addrlen, - host, sizeof (host), - service, sizeof (service), - NI_NUMERICHOST); - if (ret != 0) { - gf_log ("resolver", GF_LOG_ERROR, - "getnameinfo failed (%s)", gai_strerror (ret)); - goto err; - } - - gf_log ("resolver", GF_LOG_DEBUG, - "next DNS query will return: ip-%s port-%s", host, service); + if (!fqdn) { + gf_log_callingfn("resolver", GF_LOG_CRITICAL, + "Allocation failed for the host address"); + goto out; + } + + from_cache = _gf_false; +out: + /* Insert into the cache */ + if (fqdn && !from_cache && ip) { + struct dnscache_entry *entry = gf_dnscache_entry_init(); + + if (entry) { + entry->fqdn = fqdn; + entry->ip = gf_strdup(ip); + entry->timestamp = gf_time(); + entrydata = bin_to_data(entry, sizeof(*entry)); + dict_set(cache, (char *)ip, entrydata); } + } + return fqdn; +} - return 0; +struct xldump { + int lineno; +}; -err: - freeaddrinfo (cache->first); - cache->first = cache->next = NULL; - GF_FREE (cache); - *dnscache = NULL; - return -1; +/* to catch any format discrepencies that may arise in code */ +static int +nprintf(struct xldump *dump, const char *fmt, ...) + __attribute__((__format__(__printf__, 2, 3))); +static int +nprintf(struct xldump *dump, const char *fmt, ...) +{ + va_list ap; + char *msg = NULL; + char header[32]; + int ret = 0; + + ret = snprintf(header, 32, "%3d:", ++dump->lineno); + if (ret < 0) + goto out; + + va_start(ap, fmt); + ret = vasprintf(&msg, fmt, ap); + va_end(ap); + if (-1 == ret) + goto out; + + /* NOTE: No ret value from gf_msg_plain, so unable to compute printed + * characters. The return value from nprintf is not used, so for now + * living with it */ + gf_msg_plain(GF_LOG_WARNING, "%s %s", header, msg); + +out: + FREE(msg); + return 0; +} + +static int +xldump_options(dict_t *this, char *key, data_t *value, void *d) +{ + nprintf(d, " option %s %s", key, value->data); + return 0; } +static void +xldump_subvolumes(xlator_t *this, void *d) +{ + xlator_list_t *subv = NULL; + int len = 0; + char *subvstr = NULL; + + if (!this->children) + return; + + for (subv = this->children; subv; subv = subv->next) + len += (strlen(subv->xlator->name) + 1); + + subvstr = GF_MALLOC(len, gf_common_mt_strdup); + + len = 0; + for (subv = this->children; subv; subv = subv->next) + len += sprintf(subvstr + len, "%s%s", subv->xlator->name, + subv->next ? " " : ""); + + nprintf(d, " subvolumes %s", subvstr); + + GF_FREE(subvstr); +} + +static void +xldump(xlator_t *each, void *d) +{ + nprintf(d, "volume %s", each->name); + nprintf(d, " type %s", each->type); + dict_foreach(each->options, xldump_options, d); + + xldump_subvolumes(each, d); + + nprintf(d, "end-volume"); + nprintf(d, " "); +} void -gf_log_volume_file (FILE *specfp) +gf_log_dump_graph(FILE *specfp, glusterfs_graph_t *graph) { - extern FILE *gf_log_logfile; - int lcount = 0; - char data[GF_UNIT_KB]; + struct xldump xld = { + 0, + }; - fseek (specfp, 0L, SEEK_SET); + gf_msg_plain(GF_LOG_WARNING, "Final graph:"); + gf_msg_plain(GF_LOG_WARNING, + "+---------------------------------------" + "---------------------------------------+"); - fprintf (gf_log_logfile, "Given volfile:\n"); - fprintf (gf_log_logfile, + xlator_foreach_depth_first(graph->top, xldump, &xld); + + gf_msg_plain(GF_LOG_WARNING, "+---------------------------------------" - "---------------------------------------+\n"); - while (fgets (data, GF_UNIT_KB, specfp) != NULL){ - lcount++; - fprintf (gf_log_logfile, "%3d: %s", lcount, data); - } - fprintf (gf_log_logfile, - "\n+---------------------------------------" - "---------------------------------------+\n"); - fflush (gf_log_logfile); - fseek (specfp, 0L, SEEK_SET); + "---------------------------------------+"); } static void -gf_dump_config_flags (int fd) +gf_dump_config_flags() { - int ret = 0; - - ret = write (fd, "configuration details:\n", 23); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "configuration details:"); /* have argp */ #ifdef HAVE_ARGP - ret = write (fd, "argp 1\n", 7); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "argp 1"); #endif /* ifdef if found backtrace */ #ifdef HAVE_BACKTRACE - ret = write (fd, "backtrace 1\n", 12); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "backtrace 1"); #endif /* Berkeley-DB version has cursor->get() */ #ifdef HAVE_BDB_CURSOR_GET - ret = write (fd, "bdb->cursor->get 1\n", 19); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "bdb->cursor->get 1"); #endif /* Define to 1 if you have the <db.h> header file. */ #ifdef HAVE_DB_H - ret = write (fd, "db.h 1\n", 7); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "db.h 1"); #endif /* Define to 1 if you have the <dlfcn.h> header file. */ #ifdef HAVE_DLFCN_H - ret = write (fd, "dlfcn 1\n", 8); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "dlfcn 1"); #endif /* define if fdatasync exists */ #ifdef HAVE_FDATASYNC - ret = write (fd, "fdatasync 1\n", 12); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "fdatasync 1"); #endif /* Define to 1 if you have the `pthread' library (-lpthread). */ #ifdef HAVE_LIBPTHREAD - ret = write (fd, "libpthread 1\n", 13); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "libpthread 1"); #endif /* define if llistxattr exists */ #ifdef HAVE_LLISTXATTR - ret = write (fd, "llistxattr 1\n", 13); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "llistxattr 1"); #endif /* define if found setfsuid setfsgid */ #ifdef HAVE_SET_FSID - ret = write (fd, "setfsid 1\n", 10); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "setfsid 1"); #endif /* define if found spinlock */ #ifdef HAVE_SPINLOCK - ret = write (fd, "spinlock 1\n", 11); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "spinlock 1"); #endif /* Define to 1 if you have the <sys/epoll.h> header file. */ #ifdef HAVE_SYS_EPOLL_H - ret = write (fd, "epoll.h 1\n", 10); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "epoll.h 1"); #endif /* Define to 1 if you have the <sys/extattr.h> header file. */ #ifdef HAVE_SYS_EXTATTR_H - ret = write (fd, "extattr.h 1\n", 12); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "extattr.h 1"); #endif /* Define to 1 if you have the <sys/xattr.h> header file. */ #ifdef HAVE_SYS_XATTR_H - ret = write (fd, "xattr.h 1\n", 10); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "xattr.h 1"); #endif /* define if found st_atim.tv_nsec */ #ifdef HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC - ret = write (fd, "st_atim.tv_nsec 1\n", 18); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "st_atim.tv_nsec 1"); #endif /* define if found st_atimespec.tv_nsec */ #ifdef HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC - ret = write (fd, "st_atimespec.tv_nsec 1\n",23); - if (ret == -1) - goto out; + gf_msg_plain_nomem(GF_LOG_ALERT, "st_atimespec.tv_nsec 1"); #endif /* Define to the full name and version of this package. */ #ifdef PACKAGE_STRING - { - char msg[128]; - sprintf (msg, "package-string: %s\n", PACKAGE_STRING); - ret = write (fd, msg, strlen (msg)); - if (ret == -1) - goto out; + { + char *msg = NULL; + int ret = -1; + + ret = gf_asprintf(&msg, "package-string: %s", PACKAGE_STRING); + if (ret >= 0) { + gf_msg_plain_nomem(GF_LOG_ALERT, msg); + GF_FREE(msg); } + } #endif -out: - return; + return; } -/* Obtain a backtrace and print it to stdout. */ -/* TODO: It looks like backtrace_symbols allocates memory, - it may be problem because mostly memory allocation/free causes 'sigsegv' */ +/* Obtain a backtrace and print it to the log */ void -gf_print_trace (int32_t signum) +gf_print_trace(int32_t signum, glusterfs_ctx_t *ctx) { - extern FILE *gf_log_logfile; - struct tm *tm = NULL; - char msg[1024] = {0,}; - char timestr[256] = {0,}; - time_t utime = 0; - int ret = 0; - int fd = 0; + char msg[1024] = { + 0, + }; + char timestr[GF_TIMESTR_SIZE] = { + 0, + }; + call_stack_t *stack = NULL; + + /* Now every gf_log call will just write to a buffer and when the + * buffer becomes full, its written to the log-file. Suppose the process + * crashes and prints the backtrace in the log-file, then the previous + * log information will still be in the buffer itself. So flush the + * contents of the buffer to the log file before printing the backtrace + * which helps in debugging. + */ + gf_log_flush(); + + gf_log_disable_suppression_before_exit(ctx); + + /* Pending frames, (if any), list them in order */ + gf_msg_plain_nomem(GF_LOG_ALERT, "pending frames:"); + { + /* FIXME: traversing stacks outside pool->lock */ + list_for_each_entry(stack, &ctx->pool->all_frames, all_frames) + { + if (stack->type == GF_OP_TYPE_FOP) + sprintf(msg, "frame : type(%d) op(%s)", stack->type, + gf_fop_list[stack->op]); + else + sprintf(msg, "frame : type(%d) op(%d)", stack->type, stack->op); - fd = fileno (gf_log_logfile); + gf_msg_plain_nomem(GF_LOG_ALERT, msg); + } + } + + sprintf(msg, "patchset: %s", GLUSTERFS_REPOSITORY_REVISION); + gf_msg_plain_nomem(GF_LOG_ALERT, msg); + + sprintf(msg, "signal received: %d", signum); + gf_msg_plain_nomem(GF_LOG_ALERT, msg); + { + /* Dump the timestamp of the crash too, so the previous logs + can be related */ + gf_time_fmt(timestr, sizeof timestr, gf_time(), gf_timefmt_FT); + gf_msg_plain_nomem(GF_LOG_ALERT, "time of crash: "); + gf_msg_plain_nomem(GF_LOG_ALERT, timestr); + } + + gf_dump_config_flags(); + gf_msg_backtrace_nomem(GF_LOG_ALERT, 200); + sprintf(msg, "---------"); + gf_msg_plain_nomem(GF_LOG_ALERT, msg); + + /* Send a signal to terminate the process */ + signal(signum, SIG_DFL); + raise(signum); +} - /* Pending frames, (if any), list them in order */ - ret = write (fd, "pending frames:\n", 16); - if (ret < 0) - goto out; +void +trap(void) +{ +} - { - glusterfs_ctx_t *ctx = glusterfs_ctx_get (); - struct list_head *trav = ((call_pool_t *)ctx->pool)->all_frames.next; - while (trav != (&((call_pool_t *)ctx->pool)->all_frames)) { - call_frame_t *tmp = (call_frame_t *)(&((call_stack_t *)trav)->frames); - if (tmp->root->type == GF_OP_TYPE_FOP) - sprintf (msg,"frame : type(%d) op(%s)\n", - tmp->root->type, - gf_fop_list[tmp->root->op]); - if (tmp->root->type == GF_OP_TYPE_MGMT) - sprintf (msg,"frame : type(%d) op(%s)\n", - tmp->root->type, - gf_mgmt_list[tmp->root->op]); - - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; - - trav = trav->next; - } - ret = write (fd, "\n", 1); - if (ret < 0) - goto out; - } +char * +gf_trim(char *string) +{ + register char *s, *t; - sprintf (msg, "patchset: %s\n", GLUSTERFS_REPOSITORY_REVISION); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; + if (string == NULL) { + return NULL; + } - sprintf (msg, "signal received: %d\n", signum); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; + for (s = string; isspace(*s); s++) + ; - { - /* Dump the timestamp of the crash too, so the previous logs - can be related */ - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, "%Y-%m-%d %H:%M:%S\n", tm); - ret = write (fd, "time of crash: ", 15); - if (ret < 0) - goto out; - ret = write (fd, timestr, strlen (timestr)); - if (ret < 0) - goto out; - } + if (*s == 0) + return s; - gf_dump_config_flags (fd); -#if HAVE_BACKTRACE - /* Print 'backtrace' */ - { - void *array[200]; - size_t size; - - size = backtrace (array, 200); - backtrace_symbols_fd (&array[1], size-1, fd); - sprintf (msg, "---------\n"); - ret = write (fd, msg, strlen (msg)); - if (ret < 0) - goto out; - } -#endif /* HAVE_BACKTRACE */ + t = s + strlen(s) - 1; + while (t > s && isspace(*t)) + t--; + *++t = '\0'; -out: - /* Send a signal to terminate the process */ - signal (signum, SIG_DFL); - raise (signum); + return s; } -void -trap (void) +int +gf_strstr(const char *str, const char *delim, const char *match) { + char *tmp = NULL; + char *save_ptr = NULL; + char *tmp_str = NULL; + int ret = 0; + + tmp_str = strdup(str); + + if (str == NULL || delim == NULL || match == NULL || tmp_str == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + ret = -1; + goto out; + } + + tmp = strtok_r(tmp_str, delim, &save_ptr); + + while (tmp) { + ret = strcmp(tmp, match); + + if (ret == 0) + break; + + tmp = strtok_r(NULL, delim, &save_ptr); + } + +out: + free(tmp_str); + + return ret; } -char * -gf_trim (char *string) +int +gf_volume_name_validate(const char *volume_name) { - register char *s, *t; + const char *vname = NULL; - if (string == NULL) { - return NULL; - } + if (volume_name == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + return -1; + } - for (s = string; isspace (*s); s++) - ; + if (!isalpha(volume_name[0])) + return 1; - if (*s == 0) - return s; + for (vname = &volume_name[1]; *vname != '\0'; vname++) { + if (!(isalnum(*vname) || *vname == '_')) + return 1; + } - t = s + strlen (s) - 1; - while (t > s && isspace (*t)) - t--; - *++t = '\0'; + return 0; +} - return s; +int +gf_string2time(const char *str, uint32_t *n) +{ + unsigned long value = 0; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } + + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } + + old_errno = errno; + errno = 0; + value = strtol(str, &tail, 0); + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; + + if (errno == 0) + errno = old_errno; + + if (((tail[0] == '\0') || ((tail[0] == 's') && (tail[1] == '\0')) || + ((tail[0] == 's') && (tail[1] == 'e') && (tail[2] == 'c') && + (tail[3] == '\0')))) + goto out; + + else if (((tail[0] == 'm') && (tail[1] == '\0')) || + ((tail[0] == 'm') && (tail[1] == 'i') && (tail[2] == 'n') && + (tail[3] == '\0'))) { + value = value * GF_MINUTE_IN_SECONDS; + goto out; + } + + else if (((tail[0] == 'h') && (tail[1] == '\0')) || + ((tail[0] == 'h') && (tail[1] == 'r') && (tail[2] == '\0'))) { + value = value * GF_HOUR_IN_SECONDS; + goto out; + } + + else if (((tail[0] == 'd') && (tail[1] == '\0')) || + ((tail[0] == 'd') && (tail[1] == 'a') && (tail[2] == 'y') && + (tail[3] == 's') && (tail[4] == '\0'))) { + value = value * GF_DAY_IN_SECONDS; + goto out; + } + + else if (((tail[0] == 'w') && (tail[1] == '\0')) || + ((tail[0] == 'w') && (tail[1] == 'k') && (tail[2] == '\0'))) { + value = value * GF_WEEK_IN_SECONDS; + goto out; + } else { + return -1; + } + +out: + *n = value; + + return 0; } int -gf_strsplit (const char *str, const char *delim, - char ***tokens, int *token_count) +gf_string2percent(const char *str, double *n) { - char *_running = NULL; - char *running = NULL; - char *token = NULL; - char **token_list = NULL; - int count = 0; - int i = 0; - int j = 0; + double value = 0; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } + + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } + + old_errno = errno; + errno = 0; + value = strtod(str, &tail); + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; - if (str == NULL || delim == NULL || tokens == NULL || token_count == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - return -1; - } + if (errno == 0) + errno = old_errno; - _running = gf_strdup (str); - if (_running == NULL) - return -1; + if (!((tail[0] == '\0') || ((tail[0] == '%') && (tail[1] == '\0')))) + return -1; - running = _running; + *n = value; - while ((token = strsep (&running, delim)) != NULL) { - if (token[0] != '\0') - count++; - } - GF_FREE (_running); + return 0; +} - _running = gf_strdup (str); - if (_running == NULL) - return -1; +static int +_gf_string2long(const char *str, long *n, int base) +{ + long value = 0; + char *tail = NULL; + int old_errno = 0; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } - running = _running; + old_errno = errno; + errno = 0; + value = strtol(str, &tail, base); + if (str == tail) + errno = EINVAL; - if ((token_list = GF_CALLOC (count, sizeof (char *), - gf_common_mt_char)) == NULL) { - GF_FREE (_running); - return -1; - } + if (errno == ERANGE || errno == EINVAL) + return -1; - while ((token = strsep (&running, delim)) != NULL) { - if (token[0] == '\0') - continue; + if (errno == 0) + errno = old_errno; - token_list[i] = gf_strdup (token); - if (token_list[i] == NULL) - goto free_exit; - i++; - } + if (tail[0] != '\0') + return -1; - GF_FREE (_running); + *n = value; - *tokens = token_list; - *token_count = count; - return 0; + return 0; +} + +static int +_gf_string2ulong(const char *str, unsigned long *n, int base) +{ + unsigned long value = 0; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } + + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } + + old_errno = errno; + errno = 0; + value = strtoul(str, &tail, base); + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; -free_exit: - GF_FREE (_running); - for (j = 0; j < i; j++) - GF_FREE (token_list[j]); + if (errno == 0) + errno = old_errno; - GF_FREE (token_list); + if (tail[0] != '\0') return -1; + + *n = value; + + return 0; } -int -gf_strstr (const char *str, const char *delim, const char *match) +static int +_gf_string2uint(const char *str, unsigned int *n, int base) { - char *tmp = NULL; - char *save_ptr = NULL; - char *tmp_str = NULL; + unsigned long value = 0; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } + + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } + + old_errno = errno; + errno = 0; + value = strtoul(str, &tail, base); + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; - int ret = 0; + if (errno == 0) + errno = old_errno; - tmp_str = strdup (str); + if (tail[0] != '\0') + return -1; - if (str == NULL || delim == NULL || match == NULL || tmp_str == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - ret = -1; - goto out; - } + *n = (unsigned int)value; + return 0; +} - tmp = strtok_r (tmp_str, delim, &save_ptr); +static int +_gf_string2double(const char *str, double *n) +{ + double value = 0.0; + char *tail = NULL; + int old_errno = 0; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } - while (tmp) { - ret = strcmp (tmp, match); + old_errno = errno; + errno = 0; + value = strtod(str, &tail); + if (str == tail) + errno = EINVAL; - if (ret == 0) - break; + if (errno == ERANGE || errno == EINVAL) + return -1; - tmp = strtok_r (NULL, delim, &save_ptr); - } + if (errno == 0) + errno = old_errno; -out: - if (tmp_str) - free (tmp_str); + if (tail[0] != '\0') + return -1; - return ret; + *n = value; + return 0; } -int -gf_volume_name_validate (const char *volume_name) +static int +_gf_string2longlong(const char *str, long long *n, int base) { - const char *vname = NULL; + long long value = 0; + char *tail = NULL; + int old_errno = 0; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } - if (volume_name == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - return -1; - } + old_errno = errno; + errno = 0; + value = strtoll(str, &tail, base); + if (str == tail) + errno = EINVAL; - if (!isalpha (volume_name[0])) - return 1; + if (errno == ERANGE || errno == EINVAL) + return -1; - for (vname = &volume_name[1]; *vname != '\0'; vname++) { - if (!(isalnum (*vname) || *vname == '_')) - return 1; - } + if (errno == 0) + errno = old_errno; - return 0; + if (tail[0] != '\0') + return -1; + + *n = value; + + return 0; } +static int +_gf_string2ulonglong(const char *str, unsigned long long *n, int base) +{ + unsigned long long value = 0; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } + + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } + + old_errno = errno; + errno = 0; + value = strtoull(str, &tail, base); + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; + + if (errno == 0) + errno = old_errno; + + if (tail[0] != '\0') + return -1; + + *n = value; + + return 0; +} int -gf_string2time (const char *str, uint32_t *n) +gf_string2long(const char *str, long *n) { - unsigned long value = 0; - char *tail = NULL; - int old_errno = 0; - const char *s = NULL; - - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + return _gf_string2long(str, n, 0); +} - for (s = str; *s != '\0'; s++) { - if (isspace (*s)) - continue; - if (*s == '-') - return -1; - break; - } +int +gf_string2ulong(const char *str, unsigned long *n) +{ + return _gf_string2ulong(str, n, 0); +} - old_errno = errno; - errno = 0; - value = strtol (str, &tail, 0); +int +gf_string2int(const char *str, int *n) +{ + long l = 0; + int ret = 0; - if (errno == ERANGE || errno == EINVAL) - return -1; + ret = _gf_string2long(str, &l, 0); - if (errno == 0) - errno = old_errno; + *n = l; + return ret; +} - if (!((tail[0] == '\0') || - ((tail[0] == 's') && (tail[1] == '\0')) || - ((tail[0] == 's') && (tail[1] == 'e') && - (tail[2] == 'c') && (tail[3] == '\0')))) - return -1; +int +gf_string2uint(const char *str, unsigned int *n) +{ + return _gf_string2uint(str, n, 0); +} - *n = value; +int +gf_string2double(const char *str, double *n) +{ + return _gf_string2double(str, n); +} - return 0; +int +gf_string2longlong(const char *str, long long *n) +{ + return _gf_string2longlong(str, n, 0); } +int +gf_string2ulonglong(const char *str, unsigned long long *n) +{ + return _gf_string2ulonglong(str, n, 0); +} int -gf_string2percent (const char *str, uint32_t *n) +gf_string2int8(const char *str, int8_t *n) { - unsigned long value = 0; - char *tail = NULL; - int old_errno = 0; - const char *s = NULL; + long l = 0L; + int rv = 0; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + rv = _gf_string2long(str, &l, 0); + if (rv != 0) + return rv; - for (s = str; *s != '\0'; s++) { - if (isspace (*s)) - continue; - if (*s == '-') - return -1; - break; - } + if ((l >= INT8_MIN) && (l <= INT8_MAX)) { + *n = (int8_t)l; + return 0; + } - old_errno = errno; - errno = 0; - value = strtol (str, &tail, 0); + errno = ERANGE; + return -1; +} - if (errno == ERANGE || errno == EINVAL) - return -1; +int +gf_string2int16(const char *str, int16_t *n) +{ + long l = 0L; + int rv = 0; + + rv = _gf_string2long(str, &l, 0); + if (rv != 0) + return rv; + + if ((l >= INT16_MIN) && (l <= INT16_MAX)) { + *n = (int16_t)l; + return 0; + } - if (errno == 0) - errno = old_errno; + errno = ERANGE; + return -1; +} - if (!((tail[0] == '\0') || - ((tail[0] == '%') && (tail[1] == '\0')))) - return -1; +int +gf_string2int32(const char *str, int32_t *n) +{ + long l = 0L; + int rv = 0; - *n = value; + rv = _gf_string2long(str, &l, 0); + if (rv != 0) + return rv; + if ((l >= INT32_MIN) && (l <= INT32_MAX)) { + *n = (int32_t)l; return 0; + } + + errno = ERANGE; + return -1; } +int +gf_string2int64(const char *str, int64_t *n) +{ + long long l = 0LL; + int rv = 0; -static int -_gf_string2long (const char *str, long *n, int base) + rv = _gf_string2longlong(str, &l, 0); + if (rv != 0) + return rv; + + *n = (int64_t)l; + return 0; +} + +int +gf_string2uint8(const char *str, uint8_t *n) { - long value = 0; - char *tail = NULL; - int old_errno = 0; + unsigned long l = 0L; + int rv = 0; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + rv = _gf_string2ulong(str, &l, 0); + if (rv != 0) + return rv; - old_errno = errno; - errno = 0; - value = strtol (str, &tail, base); + if (l <= UINT8_MAX) { + *n = (uint8_t)l; + return 0; + } + + errno = ERANGE; + return -1; +} + +int +gf_string2uint16(const char *str, uint16_t *n) +{ + unsigned long l = 0L; + int rv = 0; + + rv = _gf_string2ulong(str, &l, 0); + if (rv != 0) + return rv; - if (errno == ERANGE || errno == EINVAL) - return -1; + if (l <= UINT16_MAX) { + *n = (uint16_t)l; + return 0; + } - if (errno == 0) - errno = old_errno; + errno = ERANGE; + return -1; +} - if (tail[0] != '\0') - return -1; +int +gf_string2uint32(const char *str, uint32_t *n) +{ + unsigned long l = 0L; + int rv = 0; - *n = value; + rv = _gf_string2ulong(str, &l, 0); + if (rv != 0) + return rv; + if (l <= UINT32_MAX) { + *n = (uint32_t)l; return 0; + } + + errno = ERANGE; + return -1; } -static int -_gf_string2ulong (const char *str, unsigned long *n, int base) +int +gf_string2uint64(const char *str, uint64_t *n) { - unsigned long value = 0; - char *tail = NULL; - int old_errno = 0; - const char *s = NULL; + unsigned long long l = 0ULL; + int rv = 0; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + rv = _gf_string2ulonglong(str, &l, 0); + if (rv != 0) + return rv; - for (s = str; *s != '\0'; s++) { - if (isspace (*s)) - continue; - if (*s == '-') - return -1; - break; - } + if (l <= UINT64_MAX) { + *n = (uint64_t)l; + return 0; + } - old_errno = errno; - errno = 0; - value = strtoul (str, &tail, base); + errno = ERANGE; + return -1; +} - if (errno == ERANGE || errno == EINVAL) - return -1; +int +gf_string2ulong_base10(const char *str, unsigned long *n) +{ + return _gf_string2ulong(str, n, 10); +} - if (errno == 0) - errno = old_errno; +int +gf_string2uint_base10(const char *str, unsigned int *n) +{ + return _gf_string2uint(str, n, 10); +} - if (tail[0] != '\0') - return -1; +int +gf_string2uint8_base10(const char *str, uint8_t *n) +{ + unsigned long l = 0L; + int rv = 0; - *n = value; + rv = _gf_string2ulong(str, &l, 10); + if (rv != 0) + return rv; + if (l <= UINT8_MAX) { + *n = (uint8_t)l; return 0; + } + + errno = ERANGE; + return -1; } -static int -_gf_string2uint (const char *str, unsigned int *n, int base) +int +gf_string2uint16_base10(const char *str, uint16_t *n) { - unsigned long value = 0; - char *tail = NULL; - int old_errno = 0; - const char *s = NULL; + unsigned long l = 0L; + int rv = 0; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + rv = _gf_string2ulong(str, &l, 10); + if (rv != 0) + return rv; - for (s = str; *s != '\0'; s++) { - if (isspace (*s)) - continue; - if (*s == '-') - return -1; - break; - } + if (l <= UINT16_MAX) { + *n = (uint16_t)l; + return 0; + } - old_errno = errno; - errno = 0; - value = strtoul (str, &tail, base); + errno = ERANGE; + return -1; +} - if (errno == ERANGE || errno == EINVAL) - return -1; +int +gf_string2uint32_base10(const char *str, uint32_t *n) +{ + unsigned long l = 0L; + int rv = 0; - if (errno == 0) - errno = old_errno; + rv = _gf_string2ulong(str, &l, 10); + if (rv != 0) + return rv; - if (tail[0] != '\0') - return -1; + if (l <= UINT32_MAX) { + *n = (uint32_t)l; + return 0; + } - *n = (unsigned int)value; + errno = ERANGE; + return -1; +} +int +gf_string2uint64_base10(const char *str, uint64_t *n) +{ + unsigned long long l = 0ULL; + int rv = 0; + + rv = _gf_string2ulonglong(str, &l, 10); + if (rv != 0) + return rv; + + if (l <= UINT64_MAX) { + *n = (uint64_t)l; return 0; + } + + errno = ERANGE; + return -1; } -static int -_gf_string2double (const char *str, double *n) +char * +gf_uint64_2human_readable(uint64_t n) { - double value = 0.0; - char *tail = NULL; - int old_errno = 0; + int ret = 0; + char *str = NULL; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + if (n >= GF_UNIT_PB) { + ret = gf_asprintf(&str, "%.1lfPB", ((double)n) / GF_UNIT_PB); + if (ret < 0) + goto err; + } else if (n >= GF_UNIT_TB) { + ret = gf_asprintf(&str, "%.1lfTB", ((double)n) / GF_UNIT_TB); + if (ret < 0) + goto err; + } else if (n >= GF_UNIT_GB) { + ret = gf_asprintf(&str, "%.1lfGB", ((double)n) / GF_UNIT_GB); + if (ret < 0) + goto err; + } else if (n >= GF_UNIT_MB) { + ret = gf_asprintf(&str, "%.1lfMB", ((double)n) / GF_UNIT_MB); + if (ret < 0) + goto err; + } else if (n >= GF_UNIT_KB) { + ret = gf_asprintf(&str, "%.1lfKB", ((double)n) / GF_UNIT_KB); + if (ret < 0) + goto err; + } else { + ret = gf_asprintf(&str, "%" PRIu64 "Bytes", n); + if (ret < 0) + goto err; + } + return str; +err: + return NULL; +} - old_errno = errno; - errno = 0; - value = strtod (str, &tail); +int +gf_string2bytesize_range(const char *str, uint64_t *n, uint64_t umax) +{ + double value = 0.0; + int64_t int_value = 0; + uint64_t unit = 0; + int64_t max = 0; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + gf_boolean_t fraction = _gf_false; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } - if (errno == ERANGE || errno == EINVAL) - return -1; + max = umax & 0x7fffffffffffffffLL; - if (errno == 0) - errno = old_errno; + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } - if (tail[0] != '\0') - return -1; + if (strrchr(str, '.')) + fraction = _gf_true; - *n = value; + old_errno = errno; + errno = 0; + if (fraction) + value = strtod(str, &tail); + else + int_value = strtoll(str, &tail, 10); - return 0; + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; + + if (errno == 0) + errno = old_errno; + + if (tail[0] != '\0') { + if (strcasecmp(tail, GF_UNIT_KB_STRING) == 0) + unit = GF_UNIT_KB; + else if (strcasecmp(tail, GF_UNIT_MB_STRING) == 0) + unit = GF_UNIT_MB; + else if (strcasecmp(tail, GF_UNIT_GB_STRING) == 0) + unit = GF_UNIT_GB; + else if (strcasecmp(tail, GF_UNIT_TB_STRING) == 0) + unit = GF_UNIT_TB; + else if (strcasecmp(tail, GF_UNIT_PB_STRING) == 0) + unit = GF_UNIT_PB; + else if (strcasecmp(tail, GF_UNIT_B_STRING) != 0) + return -1; + + if (unit > 0) { + if (fraction) + value *= unit; + else + int_value *= unit; + } + } + + if (fraction) { + if ((max - value) < 0) { + errno = ERANGE; + return -1; + } + *n = (uint64_t)value; + } else { + if ((max - int_value) < 0) { + errno = ERANGE; + return -1; + } + *n = int_value; + } + + return 0; } -static int -_gf_string2longlong (const char *str, long long *n, int base) +int +gf_string2bytesize_uint64(const char *str, uint64_t *n) { - long long value = 0; - char *tail = NULL; - int old_errno = 0; + return gf_string2bytesize_range(str, n, UINT64_MAX); +} - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } +int +gf_string2bytesize_int64(const char *str, int64_t *n) +{ + uint64_t u64 = 0; + int ret = 0; - old_errno = errno; - errno = 0; - value = strtoll (str, &tail, base); + ret = gf_string2bytesize_range(str, &u64, INT64_MAX); + *n = (int64_t)u64; + return ret; +} - if (errno == ERANGE || errno == EINVAL) - return -1; +int +gf_string2percent_or_bytesize(const char *str, double *n, + gf_boolean_t *is_percent) +{ + double value = 0ULL; + char *tail = NULL; + int old_errno = 0; + const char *s = NULL; + + if (str == NULL || n == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + errno = EINVAL; + return -1; + } + + for (s = str; *s != '\0'; s++) { + if (isspace(*s)) + continue; + if (*s == '-') + return -1; + break; + } + + old_errno = errno; + errno = 0; + value = strtod(str, &tail); + if (str == tail) + errno = EINVAL; + + if (errno == ERANGE || errno == EINVAL) + return -1; - if (errno == 0) - errno = old_errno; + if (errno == 0) + errno = old_errno; + + /*Maximum accepted value for 64 bit OS will be (2^14 -1)PB*/ + if (tail[0] != '\0') { + if (strcasecmp(tail, GF_UNIT_KB_STRING) == 0) + value *= GF_UNIT_KB; + else if (strcasecmp(tail, GF_UNIT_MB_STRING) == 0) + value *= GF_UNIT_MB; + else if (strcasecmp(tail, GF_UNIT_GB_STRING) == 0) + value *= GF_UNIT_GB; + else if (strcasecmp(tail, GF_UNIT_TB_STRING) == 0) + value *= GF_UNIT_TB; + else if (strcasecmp(tail, GF_UNIT_PB_STRING) == 0) + value *= GF_UNIT_PB; + else if (strcasecmp(tail, GF_UNIT_PERCENT_STRING) == 0) + *is_percent = _gf_true; + else + return -1; + } - if (tail[0] != '\0') - return -1; + /* Error out if we cannot store the value in uint64 */ + if ((UINT64_MAX - value) < 0) { + errno = ERANGE; + return -1; + } - *n = value; + *n = value; - return 0; + return 0; } -static int -_gf_string2ulonglong (const char *str, unsigned long long *n, int base) +int64_t +gf_str_to_long_long(const char *number) { - unsigned long long value = 0; - char *tail = NULL; - int old_errno = 0; - const char *s = NULL; + int64_t unit = 1; + int64_t ret = 0; + char *endptr = NULL; + if (!number) + return 0; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + ret = strtoll(number, &endptr, 0); - for (s = str; *s != '\0'; s++) { - if (isspace (*s)) - continue; - if (*s == '-') - return -1; + if (endptr) { + switch (*endptr) { + case 'G': + case 'g': + if ((*(endptr + 1) == 'B') || (*(endptr + 1) == 'b')) + unit = 1024 * 1024 * 1024; + break; + case 'M': + case 'm': + if ((*(endptr + 1) == 'B') || (*(endptr + 1) == 'b')) + unit = 1024 * 1024; + break; + case 'K': + case 'k': + if ((*(endptr + 1) == 'B') || (*(endptr + 1) == 'b')) + unit = 1024; + break; + case '%': + unit = 1; + break; + default: + unit = 1; break; } + } + return ret * unit; +} - old_errno = errno; - errno = 0; - value = strtoull (str, &tail, base); +int +gf_string2boolean(const char *str, gf_boolean_t *b) +{ + if (str == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + return -1; + } - if (errno == ERANGE || errno == EINVAL) - return -1; + if ((strcasecmp(str, "1") == 0) || (strcasecmp(str, "on") == 0) || + (strcasecmp(str, "yes") == 0) || (strcasecmp(str, "true") == 0) || + (strcasecmp(str, "enable") == 0)) { + *b = _gf_true; + return 0; + } - if (errno == 0) - errno = old_errno; + if ((strcasecmp(str, "0") == 0) || (strcasecmp(str, "off") == 0) || + (strcasecmp(str, "no") == 0) || (strcasecmp(str, "false") == 0) || + (strcasecmp(str, "disable") == 0)) { + *b = _gf_false; + return 0; + } - if (tail[0] != '\0') - return -1; + return -1; +} - *n = value; +int +gf_strn2boolean(const char *str, const int len, gf_boolean_t *b) +{ + if (str == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + return -1; + } - return 0; + switch (len) { + case 1: + if (strcasecmp(str, "1") == 0) { + *b = _gf_true; + return 0; + } else if (strcasecmp(str, "0") == 0) { + *b = _gf_false; + return 0; + } + break; + case 2: + if (strcasecmp(str, "on") == 0) { + *b = _gf_true; + return 0; + } else if (strcasecmp(str, "no") == 0) { + *b = _gf_false; + return 0; + } + break; + case 3: + if (strcasecmp(str, "yes") == 0) { + *b = _gf_true; + return 0; + } else if (strcasecmp(str, "off") == 0) { + *b = _gf_false; + return 0; + } + break; + case 4: + if (strcasecmp(str, "true") == 0) { + *b = _gf_true; + return 0; + } + break; + case 5: + if (strcasecmp(str, "false") == 0) { + *b = _gf_false; + return 0; + } + break; + case 6: + if (strcasecmp(str, "enable") == 0) { + *b = _gf_true; + return 0; + } + break; + case 7: + if (strcasecmp(str, "disable") == 0) { + *b = _gf_false; + return 0; + } + break; + default: + return -1; + break; + } + return -1; } int -gf_string2long (const char *str, long *n) +gf_lockfd(int fd) { - return _gf_string2long (str, n, 0); + struct gf_flock fl; + + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + return fcntl(fd, F_SETLK, &fl); } int -gf_string2ulong (const char *str, unsigned long *n) +gf_unlockfd(int fd) { - return _gf_string2ulong (str, n, 0); + struct gf_flock fl; + + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + + return fcntl(fd, F_SETLK, &fl); } +static void +compute_checksum(char *buf, const ssize_t size, uint32_t *checksum) +{ + int ret = -1; + char *checksum_buf = NULL; + + checksum_buf = (char *)(checksum); + + if (!(*checksum)) { + checksum_buf[0] = 0xba; + checksum_buf[1] = 0xbe; + checksum_buf[2] = 0xb0; + checksum_buf[3] = 0x0b; + } + + for (ret = 0; ret < (size - 4); ret += 4) { + checksum_buf[0] ^= (buf[ret]); + checksum_buf[1] ^= (buf[ret + 1] << 1); + checksum_buf[2] ^= (buf[ret + 2] << 2); + checksum_buf[3] ^= (buf[ret + 3] << 3); + } + + for (ret = 0; ret <= (size % 4); ret++) { + checksum_buf[ret] ^= (buf[(size - 4) + ret] << ret); + } + + return; +} + +#define GF_CHECKSUM_BUF_SIZE 1024 + int -gf_string2int (const char *str, int *n) +get_checksum_for_file(int fd, uint32_t *checksum, int op_version) { - long l = 0; - int ret = 0; + int ret = -1; + char buf[GF_CHECKSUM_BUF_SIZE] = { + 0, + }; + + /* goto first place */ + sys_lseek(fd, 0L, SEEK_SET); + do { + ret = sys_read(fd, &buf, GF_CHECKSUM_BUF_SIZE); + if (ret > 0) { + if (op_version < GD_OP_VERSION_5_4) + compute_checksum(buf, GF_CHECKSUM_BUF_SIZE, checksum); + else + compute_checksum(buf, ret, checksum); + } + } while (ret > 0); - ret = _gf_string2long (str, &l, 0); + /* set it back */ + sys_lseek(fd, 0L, SEEK_SET); - *n = l; - return ret; + return ret; } int -gf_string2uint (const char *str, unsigned int *n) +get_checksum_for_path(char *path, uint32_t *checksum, int op_version) { - return _gf_string2uint (str, n, 0); + int ret = -1; + int fd = -1; + + GF_ASSERT(path); + GF_ASSERT(checksum); + + fd = open(path, O_RDWR); + + if (fd == -1) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, LG_MSG_PATH_OPEN_FAILED, + "path=%s", path, NULL); + goto out; + } + + ret = get_checksum_for_file(fd, checksum, op_version); + +out: + if (fd != -1) + sys_close(fd); + + return ret; } +/** + * get_file_mtime -- Given a path, get the mtime for the file + * + * @path: The filepath to check the mtime on + * @stamp: The parameter to set after we get the mtime + * + * @returns: success: 0 + * errors : Errors returned by the stat () call + */ int -gf_string2double (const char *str, double *n) +get_file_mtime(const char *path, time_t *stamp) { - return _gf_string2double (str, n); + struct stat f_stat = {0}; + int ret = -EINVAL; + + GF_VALIDATE_OR_GOTO(THIS->name, path, out); + GF_VALIDATE_OR_GOTO(THIS->name, stamp, out); + + ret = sys_stat(path, &f_stat); + if (ret < 0) { + gf_smsg(THIS->name, GF_LOG_ERROR, errno, LG_MSG_FILE_STAT_FAILED, + "path=%s", path, NULL); + goto out; + } + + /* Set the mtime */ + *stamp = f_stat.st_mtime; +out: + return ret; } -int -gf_string2longlong (const char *str, long long *n) +/** + * gf_is_ip_in_net -- Checks if an IP Address is in a network. + * A network should be specified by something like + * '10.5.153.0/24' (in CIDR notation). + * + * @result : Sets to true if the IP is in the network + * @ip_str : The IP to check + * @network: The network to check the IP against. + * + * @return: success: _gf_true + * failure: -EINVAL for bad args, retval of inet_pton otherwise + */ +gf_boolean_t +gf_is_ip_in_net(const char *network, const char *ip_str) { - return _gf_string2longlong (str, n, 0); + unsigned long ip_buf = 0; + unsigned long net_ip_buf = 0; + unsigned long subnet_mask = 0; + int ret = -EINVAL; + char *slash = NULL; + char *net_ip = NULL; + char *subnet = NULL; + char *net_str = NULL; + int family = AF_INET; + gf_boolean_t result = _gf_false; + + GF_ASSERT(network); + GF_ASSERT(ip_str); + + if (strchr(network, ':')) + family = AF_INET6; + else if (strchr(network, '.')) + family = AF_INET; + else { + goto out; + } + + net_str = strdupa(network); + slash = strchr(net_str, '/'); + if (!slash) + goto out; + *slash = '\0'; + + subnet = slash + 1; + net_ip = net_str; + + /* Convert IP address to a long */ + ret = inet_pton(family, ip_str, &ip_buf); + if (ret < 0) + gf_smsg("common-utils", GF_LOG_ERROR, errno, LG_MSG_INET_PTON_FAILED, + NULL); + + /* Convert network IP address to a long */ + ret = inet_pton(family, net_ip, &net_ip_buf); + if (ret < 0) { + gf_smsg("common-utils", GF_LOG_ERROR, errno, LG_MSG_INET_PTON_FAILED, + NULL); + goto out; + } + + /* Converts /x into a mask */ + subnet_mask = (1 << atoi(subnet)) - 1; + + result = ((ip_buf & subnet_mask) == (net_ip_buf & subnet_mask)); +out: + return result; } -int -gf_string2ulonglong (const char *str, unsigned long long *n) +char * +strtail(char *str, const char *pattern) { - return _gf_string2ulonglong (str, n, 0); + int i = 0; + + for (i = 0; str[i] == pattern[i] && str[i]; i++) + ; + + if (pattern[i] == '\0') + return str + i; + + return NULL; } -int -gf_string2int8 (const char *str, int8_t *n) +void +skipwhite(char **s) { - long l = 0L; - int rv = 0; + while (isspace(**s)) + (*s)++; +} - rv = _gf_string2long (str, &l, 0); - if (rv != 0) - return rv; +void +gf_strTrim(char **s) +{ + char *end = NULL; - if (l >= INT8_MIN && l <= INT8_MAX) { - *n = (int8_t) l; - return 0; - } + end = *s + strlen(*s) - 1; + while (end > *s && isspace((unsigned char)*end)) + end--; - errno = ERANGE; - return -1; + *(end + 1) = '\0'; + + while (isspace(**s)) + (*s)++; + + return; } -int -gf_string2int16 (const char *str, int16_t *n) +char * +nwstrtail(char *str, char *pattern) { - long l = 0L; - int rv = 0; + for (;;) { + skipwhite(&str); + skipwhite(&pattern); - rv = _gf_string2long (str, &l, 0); - if (rv != 0) - return rv; + if (*str != *pattern || !*str) + break; - if (l >= INT16_MIN && l <= INT16_MAX) { - *n = (int16_t) l; - return 0; - } + str++; + pattern++; + } - errno = ERANGE; - return -1; + return *pattern ? NULL : str; } -int -gf_string2int32 (const char *str, int32_t *n) +/** + * token_iter_init -- initialize tokenization + * + * @str: string to be tokenized + * @sep: token separator character + * @tit: pointer to iteration state + * + * @return: token string + * + * The returned token string and tit are + * not to be used directly, but through + * next_token(). + */ +char * +token_iter_init(char *str, char sep, token_iter_t *tit) { - long l = 0L; - int rv = 0; + tit->end = str + strlen(str); + tit->sep = sep; - rv = _gf_string2long (str, &l, 0); - if (rv != 0) - return rv; + return str; +} - if (l >= INT32_MIN && l <= INT32_MAX) { - *n = (int32_t) l; - return 0; - } +/** + * next_token -- fetch next token in tokenization + * inited by token_iter_init(). + * + * @tokenp: pointer to token + * @tit: pointer to iteration state + * + * @return: true if iteration ends, else false + * + * The token pointed by @tokenp can be used + * after a call to next_token(). When next_token() + * returns true the iteration is to be stopped + * and the string with which the tokenization + * was inited (see token_iter_init() is restored, + * apart from dropped tokens (see drop_token()). + */ +gf_boolean_t +next_token(char **tokenp, token_iter_t *tit) +{ + char *cursor = NULL; + gf_boolean_t is_last = _gf_false; + + for (cursor = *tokenp; *cursor; cursor++) + ; + if (cursor < tit->end) { + /* + * We detect that in between current token and end a zero + * marker has already been inserted. This means that the + * token has already been returned. We restore the + * separator and move ahead. + */ + *cursor = tit->sep; + *tokenp = cursor + 1; + } + + for (cursor = *tokenp; *cursor && *cursor != tit->sep; cursor++) + ; + /* If the cursor ended up on a zero byte, then it's the last token. */ + is_last = !*cursor; + /* Zero-terminate the token. */ + *cursor = 0; + + return is_last; +} - errno = ERANGE; - return -1; +/* + * drop_token -- drop a token during iterated calls of next_token(). + * + * Sample program that uses these functions to tokenize + * a comma-separated first argument while dropping the + * rest of the arguments if they occur as token: + * + * #include <stdio.h> + * #include <stdlib.h> + * #include <string.h> + * #include "glusterfs/common-utils.h" + * + * int + * main (int argc, char **argv) + * { + * char *buf; + * char *token; + * token_iter_t tit; + * int i; + * gf_boolean_t iter_end; + * + * if (argc <= 1) + * abort(); + * + * buf = strdup (argv[1]); + * if (!buf) + * abort(); + * + * for (token = token_iter_init (buf, ',', &tit) ;;) { + * iter_end = next_token (&token, &tit); + * printf("found token: '%s'\n", token); + * for (i = 2; i < argc; i++) { + * if (strcmp (argv[i], token) == 0) { + * printf ("%s\n", "dropping token!"); + * drop_token (token, &tit); + * break; + * } + * } + * if (iter_end) + * break; + * } + * + * printf ("finally: '%s'\n", buf); + * + * return 0; + * } + */ +void +drop_token(char *token, token_iter_t *tit) +{ + char *cursor = NULL; + + for (cursor = token; *cursor; cursor++) + ; + if (cursor < tit->end) { + /* + * We detect a zero inserted by next_token(). + * Step the cursor and copy what comes after + * to token. + */ + for (cursor++; cursor < tit->end; *token++ = *cursor++) + ; + } + + /* + * Zero out the remainder of the buffer. + * It would be enough to insert just a single zero, + * but we continue 'till the end to have cleaner + * memory content. + */ + for (cursor = token; cursor < tit->end; *cursor++ = 0) + ; + + /* Adjust the end to point to the new terminating zero. */ + tit->end = token; } -int -gf_string2int64 (const char *str, int64_t *n) +/* Syntax formed according to RFC 1912 (RFC 1123 & 952 are more restrictive) * + <hname> ::= <gen-name>*["."<gen-name>] * + <gen-name> ::= <let-or-digit> <[*[<let-or-digit-or-hyphen>]<let-or-digit>] */ +char +valid_host_name(char *address, int length) { - long long l = 0LL; - int rv = 0; + int i = 0; + int str_len = 0; + char ret = 1; + char *dup_addr = NULL; + char *temp_str = NULL; + char *save_ptr = NULL; + + if ((length > _POSIX_HOST_NAME_MAX) || (length < 1)) { + ret = 0; + goto out; + } + + dup_addr = gf_strdup(address); + if (!dup_addr) { + ret = 0; + goto out; + } + + if (!isalnum(dup_addr[length - 1]) && (dup_addr[length - 1] != '*')) { + ret = 0; + goto out; + } + + /* Check for consecutive dots, which is invalid in a hostname and is + * ignored by strtok() + */ + if (strstr(dup_addr, "..")) { + ret = 0; + goto out; + } + + /* gen-name */ + temp_str = strtok_r(dup_addr, ".", &save_ptr); + do { + str_len = strlen(temp_str); + + if (!isalnum(temp_str[0]) || !isalnum(temp_str[str_len - 1])) { + ret = 0; + goto out; + } + for (i = 1; i < str_len; i++) { + if (!isalnum(temp_str[i]) && (temp_str[i] != '-')) { + ret = 0; + goto out; + } + } + } while ((temp_str = strtok_r(NULL, ".", &save_ptr))); - rv = _gf_string2longlong (str, &l, 0); - if (rv != 0) - return rv; +out: + GF_FREE(dup_addr); + return ret; +} - if (l >= INT64_MIN && l <= INT64_MAX) { - *n = (int64_t) l; - return 0; +/* Matches all ipv4 address, if wildcard_acc is true '*' wildcard pattern for* + subnets is considered as valid strings as well */ +char +valid_ipv4_address(char *address, int length, gf_boolean_t wildcard_acc) +{ + int octets = 0; + int value = 0; + char *tmp = NULL, *ptr = NULL, *prev = NULL, *endptr = NULL; + char ret = 1; + int is_wildcard = 0; + + tmp = gf_strdup(address); + + /* + * To prevent cases where last character is '.' and which have + * consecutive dots like ".." as strtok ignore consecutive + * delimiters. + */ + if (length <= 0 || (strstr(address, "..")) || + (!isdigit(tmp[length - 1]) && (tmp[length - 1] != '*'))) { + ret = 0; + goto out; + } + + prev = strtok_r(tmp, ".", &ptr); + + while (prev != NULL) { + octets++; + if (wildcard_acc && !strcmp(prev, "*")) { + is_wildcard = 1; + } else { + value = strtol(prev, &endptr, 10); + if ((value > 255) || (value < 0) || + (endptr != NULL && *endptr != '\0')) { + ret = 0; + goto out; + } } + prev = strtok_r(NULL, ".", &ptr); + } - errno = ERANGE; - return -1; + if ((octets > 4) || (octets < 4 && !is_wildcard)) { + ret = 0; + } + +out: + GF_FREE(tmp); + return ret; } -int -gf_string2uint8 (const char *str, uint8_t *n) +char +valid_cidr_address(char *cidr_address, gf_boolean_t wildcard_acc) { - unsigned long l = 0L; - int rv = 0; + unsigned int net_mask = 0, len = 0; + char *temp = NULL, *cidr_str = NULL, ret = 1; - rv = _gf_string2ulong (str, &l, 0); - if (rv != 0) - return rv; + cidr_str = strdupa(cidr_address); + temp = strstr(cidr_str, "/"); + if (temp == NULL) + return 0; /* Since Invalid cidr ip address we return 0 */ - if (l >= 0 && l <= UINT8_MAX) { - *n = (uint8_t) l; - return 0; - } + *temp = '\0'; + temp++; + net_mask = (unsigned int)atoi(temp); - errno = ERANGE; - return -1; + if (net_mask > 32 || net_mask < 1) + return 0; /* Since Invalid cidr ip address we return 0*/ + + len = strlen(cidr_str); + + ret = valid_ipv4_address(cidr_str, len, wildcard_acc); + + return ret; } -int -gf_string2uint16 (const char *str, uint16_t *n) +/** + * valid_ipv4_subnetwork() takes the pattern and checks if it contains + * a valid ipv4 subnetwork pattern i.e. xx.xx.xx.xx/n. IPv4 address + * part (xx.xx.xx.xx) and mask bits length part (n). The mask bits length + * must be in 0-32 range (ipv4 addr is 32 bit). The pattern must be + * in this format. + * + * Returns _gf_true if both IP addr and mask bits len are valid + * _gf_false otherwise. + */ +gf_boolean_t +valid_ipv4_subnetwork(const char *address) { - unsigned long l = 0L; - int rv = 0; + char *slash = NULL; + char *paddr = NULL; + char *endptr = NULL; + long prefixlen = -1; + gf_boolean_t retv = _gf_true; + + if (address == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + return _gf_false; + } + + paddr = gf_strdup(address); + if (paddr == NULL) /* ENOMEM */ + return _gf_false; + + /* + * INVALID: If '/' is not present OR + * Nothing specified after '/' + */ + slash = strchr(paddr, '/'); + if ((slash == NULL) || (slash[1] == '\0')) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, 0, + LG_MSG_INVALID_IPV4_FORMAT, + "Invalid IPv4 " + "subnetwork format"); + retv = _gf_false; + goto out; + } + + *slash = '\0'; + retv = valid_ipv4_address(paddr, strlen(paddr), _gf_false); + if (retv == _gf_false) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, 0, + LG_MSG_INVALID_IPV4_FORMAT, + "Invalid IPv4 subnetwork address"); + goto out; + } + /* + * Reset errno before checking it + */ + errno = 0; + prefixlen = strtol(slash + 1, &endptr, 10); + if ((errno != 0) || (*endptr != '\0') || (prefixlen < 0) || + (prefixlen > IPv4_ADDR_SIZE)) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, 0, + LG_MSG_INVALID_IPV4_FORMAT, + "Invalid IPv4 subnetwork mask"); + retv = _gf_false; + goto out; + } + + retv = _gf_true; +out: + GF_FREE(paddr); + return retv; +} - rv = _gf_string2ulong (str, &l, 0); - if (rv != 0) - return rv; +char +valid_ipv6_address(char *address, int length, gf_boolean_t wildcard_acc) +{ + int hex_numbers = 0; + int value = 0; + int i = 0; + char *tmp = NULL, *ptr = NULL, *prev = NULL, *endptr = NULL; + char ret = 1; + int is_wildcard = 0; + int is_compressed = 0; + + tmp = gf_strdup(address); + + /* Check for '%' for link local addresses */ + endptr = strchr(tmp, '%'); + if (endptr) { + *endptr = '\0'; + length = strlen(tmp); + endptr = NULL; + } + + /* Check for compressed form */ + if (length <= 0 || tmp[length - 1] == ':') { + ret = 0; + goto out; + } + for (i = 0; i < (length - 1); i++) { + if (tmp[i] == ':' && tmp[i + 1] == ':') { + if (is_compressed == 0) + is_compressed = 1; + else { + ret = 0; + goto out; + } + } + } - if (l >= 0 && l <= UINT16_MAX) { - *n = (uint16_t) l; - return 0; + prev = strtok_r(tmp, ":", &ptr); + + while (prev != NULL) { + hex_numbers++; + if (wildcard_acc && !strcmp(prev, "*")) { + is_wildcard = 1; + } else { + value = strtol(prev, &endptr, 16); + if ((value > 0xffff) || (value < 0) || + (endptr != NULL && *endptr != '\0')) { + ret = 0; + goto out; + } } + prev = strtok_r(NULL, ":", &ptr); + } - errno = ERANGE; - return -1; + if ((hex_numbers > 8) || + (hex_numbers < 8 && !is_wildcard && !is_compressed)) { + ret = 0; + } + +out: + GF_FREE(tmp); + return ret; } -int -gf_string2uint32 (const char *str, uint32_t *n) +char +valid_internet_address(char *address, gf_boolean_t wildcard_acc, + gf_boolean_t cidr) { - unsigned long l = 0L; - int rv = 0; + char ret = 0; + int length = 0; - rv = _gf_string2ulong (str, &l, 0); - if (rv != 0) - return rv; + if (address == NULL) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + goto out; + } - if (l >= 0 && l <= UINT32_MAX) { - *n = (uint32_t) l; - return 0; + length = strlen(address); + if (length == 0) + goto out; + + if (cidr && valid_cidr_address(address, wildcard_acc)) { + ret = 1; + } + + if (valid_ipv4_address(address, length, wildcard_acc) || + valid_ipv6_address(address, length, wildcard_acc) || + valid_host_name(address, length)) + ret = 1; + +out: + return ret; +} + +/** + * valid_mount_auth_address - Validate the rpc-auth.addr.allow/reject pattern + * + * @param address - Pattern to be validated + * + * @return _gf_true if "address" is "*" (anonymous) 'OR' + * if "address" is valid FQDN or valid IPv4/6 address 'OR' + * if "address" contains wildcard chars e.g. "'*' or '?' or + * '['" if "address" is valid ipv4 subnet pattern (xx.xx.xx.xx/n) _gf_false + * otherwise + * + * + * NB: If the user/admin set for wildcard pattern, then it does not have + * to be validated. Make it similar to the way exportfs (kNFS) works. + */ +gf_boolean_t +valid_mount_auth_address(char *address) +{ + int length = 0; + char *cp = NULL; + + /* 1. Check for "NULL and empty string */ + if ((address == NULL) || (address[0] == '\0')) { + gf_msg_callingfn(THIS->name, GF_LOG_WARNING, EINVAL, LG_MSG_INVALID_ARG, + "argument invalid"); + return _gf_false; + } + + /* 2. Check for Anonymous */ + if (strcmp(address, "*") == 0) + return _gf_true; + + for (cp = address; *cp; cp++) { + /* 3. Check for wildcard pattern */ + if (*cp == '*' || *cp == '?' || *cp == '[') { + return _gf_true; } - errno = ERANGE; - return -1; + /* + * 4. check for IPv4 subnetwork i.e. xx.xx.xx.xx/n + * TODO: check for IPv6 subnetwork + * NB: Wildcard must not be mixed with subnetwork. + */ + if (*cp == '/') { + return valid_ipv4_subnetwork(address); + } + } + + /* 5. Check for v4/v6 IP addr and FQDN/hostname */ + length = strlen(address); + if ((valid_ipv4_address(address, length, _gf_false)) || + (valid_ipv6_address(address, length, _gf_false)) || + (valid_host_name(address, length))) { + return _gf_true; + } + + return _gf_false; } -int -gf_string2uint64 (const char *str, uint64_t *n) +/** + * gf_sock_union_equal_addr - check if two given gf_sock_unions have same addr + * + * @param a - first sock union + * @param b - second sock union + * @return _gf_true if a and b have same ipv{4,6} addr, _gf_false otherwise + */ +gf_boolean_t +gf_sock_union_equal_addr(union gf_sock_union *a, union gf_sock_union *b) { - unsigned long long l = 0ULL; - int rv = 0; + if (!a || !b) { + gf_smsg("common-utils", GF_LOG_ERROR, 0, LG_MSG_INVALID_ENTRY, + "gf_sock_union_equal_addr", NULL); + return _gf_false; + } + + if (a->storage.ss_family != b->storage.ss_family) + return _gf_false; + + switch (a->storage.ss_family) { + case AF_INET: + if (a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr) + return _gf_true; + else + return _gf_false; + + case AF_INET6: + if (memcmp((void *)(&a->sin6.sin6_addr), + (void *)(&b->sin6.sin6_addr), sizeof(a->sin6.sin6_addr))) + return _gf_false; + else + return _gf_true; + + default: + gf_msg_debug("common-utils", 0, + "Unsupported/invalid address " + "family"); + break; + } + + return _gf_false; +} - rv = _gf_string2ulonglong (str, &l, 0); - if (rv != 0) - return rv; +/* + * Check if both have same network address. + * Extract the network address from the sockaddr(s) addr by applying the + * network mask. If they match, return boolean _gf_true, _gf_false otherwise. + * + * (x == y) <=> (x ^ y == 0) + * (x & y) ^ (x & z) <=> x & (y ^ z) + * + * ((ip1 & mask) == (ip2 & mask)) <=> ((mask & (ip1 ^ ip2)) == 0) + */ +gf_boolean_t +mask_match(const uint32_t a, const uint32_t b, const uint32_t m) +{ + return (((a ^ b) & m) == 0); +} - if (l >= 0 && l <= UINT64_MAX) { - *n = (uint64_t) l; - return 0; - } +/*Thread safe conversion function*/ +char * +uuid_utoa(uuid_t uuid) +{ + char *uuid_buffer = glusterfs_uuid_buf_get(); + gf_uuid_unparse(uuid, uuid_buffer); + return uuid_buffer; +} - errno = ERANGE; - return -1; +/*Re-entrant conversion function*/ +char * +uuid_utoa_r(uuid_t uuid, char *dst) +{ + if (!dst) + return NULL; + gf_uuid_unparse(uuid, dst); + return dst; } -int -gf_string2ulong_base10 (const char *str, unsigned long *n) +/*Thread safe conversion function*/ +char * +lkowner_utoa(gf_lkowner_t *lkowner) { - return _gf_string2ulong (str, n, 10); + char *lkowner_buffer = glusterfs_lkowner_buf_get(); + lkowner_unparse(lkowner, lkowner_buffer, GF_LKOWNER_BUF_SIZE); + return lkowner_buffer; } -int -gf_string2uint_base10 (const char *str, unsigned int *n) +/*Re-entrant conversion function*/ +char * +lkowner_utoa_r(gf_lkowner_t *lkowner, char *dst, int len) { - return _gf_string2uint (str, n, 10); + if (!dst) + return NULL; + lkowner_unparse(lkowner, dst, len); + return dst; } -int -gf_string2uint8_base10 (const char *str, uint8_t *n) +gf_boolean_t +is_valid_lease_id(const char *lease_id) { - unsigned long l = 0L; - int rv = 0; + int i = 0; + gf_boolean_t valid = _gf_false; - rv = _gf_string2ulong (str, &l, 10); - if (rv != 0) - return rv; + for (i = 0; i < LEASE_ID_SIZE; i++) { + if (lease_id[i] != 0) { + valid = _gf_true; + goto out; + } + } +out: + return valid; +} - if (l >= 0 && l <= UINT8_MAX) { - *n = (uint8_t) l; - return 0; +/* Lease_id can be a either in printable or non printable binary + * format. This function can be used to print any lease_id. + * + * This function returns a pointer to a buf, containing the ascii + * representation of the value in lease_id, in the following format: + * 4hexnum-4hexnum-4hexnum-4hexnum-4hexnum-4hexnum-4hexnum-4hexnum + * + * Eg: If lease_id = "lid1-clnt1" the printable string would be: + * 6c69-6431-2d63-6c6e-7431-0000-0000-0000 + * + * Note: The pointer returned should not be stored for further use, as any + * subsequent call to this function will override the same buffer. + */ +char * +leaseid_utoa(const char *lease_id) +{ + char *buf = NULL; + int i = 0; + int j = 0; + + buf = glusterfs_leaseid_buf_get(); + if (!buf) + goto out; + + for (i = 0; i < LEASE_ID_SIZE; i++) { + if (i && !(i % 2)) { + buf[j] = '-'; + j++; } + sprintf(&buf[j], "%02hhx", lease_id[i]); + j += 2; + if (j == GF_LEASE_ID_BUF_SIZE) + break; + } + buf[GF_LEASE_ID_BUF_SIZE - 1] = '\0'; +out: + return buf; +} - errno = ERANGE; - return -1; +char * +gf_leaseid_get() +{ + return glusterfs_leaseid_buf_get(); +} + +char * +gf_existing_leaseid() +{ + return glusterfs_leaseid_exist(); +} + +void * +gf_array_elem(void *a, int index, size_t elem_size) +{ + uint8_t *ptr = a; + return (void *)(ptr + index * elem_size); +} + +void +gf_elem_swap(void *x, void *y, size_t l) +{ + uint8_t *a = x, *b = y, c; + while (l--) { + c = *a; + *a++ = *b; + *b++ = c; + } +} + +void +gf_array_insertionsort(void *A, int l, int r, size_t elem_size, gf_cmp cmp) +{ + int i = l; + int N = r + 1; + void *Temp = NULL; + int j = 0; + + for (i = l; i < N; i++) { + Temp = gf_array_elem(A, i, elem_size); + j = i - 1; + while (j >= 0 && (cmp(Temp, gf_array_elem(A, j, elem_size)) < 0)) { + gf_elem_swap(Temp, gf_array_elem(A, j, elem_size), elem_size); + Temp = gf_array_elem(A, j, elem_size); + j = j - 1; + } + } } int -gf_string2uint16_base10 (const char *str, uint16_t *n) +gf_is_str_int(const char *value) { - unsigned long l = 0L; - int rv = 0; + int flag = 0; + char *str = NULL; + char *fptr = NULL; - rv = _gf_string2ulong (str, &l, 10); - if (rv != 0) - return rv; + GF_VALIDATE_OR_GOTO(THIS->name, value, out); - if (l >= 0 && l <= UINT16_MAX) { - *n = (uint16_t) l; - return 0; + str = gf_strdup(value); + if (!str) + goto out; + + fptr = str; + + while (*str) { + if (!isdigit(*str)) { + flag = 1; + goto out; } + str++; + } - errno = ERANGE; - return -1; +out: + GF_FREE(fptr); + + return flag; +} +/* + * rounds up nr to power of two. If nr is already a power of two, just returns + * nr + */ + +int32_t +gf_roundup_power_of_two(int32_t nr) +{ + int32_t result = 1; + + if (nr < 0) { + gf_smsg("common-utils", GF_LOG_WARNING, 0, LG_MSG_NEGATIVE_NUM_PASSED, + NULL); + result = -1; + goto out; + } + + while (result < nr) + result *= 2; + +out: + return result; +} + +/* + * rounds up nr to next power of two. If nr is already a power of two, next + * power of two is returned. + */ + +int32_t +gf_roundup_next_power_of_two(int32_t nr) +{ + int32_t result = 1; + + if (nr < 0) { + gf_smsg("common-utils", GF_LOG_WARNING, 0, LG_MSG_NEGATIVE_NUM_PASSED, + NULL); + result = -1; + goto out; + } + + while (result <= nr) + result *= 2; + +out: + return result; } int -gf_string2uint32_base10 (const char *str, uint32_t *n) +validate_brick_name(char *brick) { - unsigned long l = 0L; - int rv = 0; + char *delimiter = NULL; + int ret = 0; + delimiter = strrchr(brick, ':'); + if (!delimiter || delimiter == brick || *(delimiter + 1) != '/') + ret = -1; - rv = _gf_string2ulong (str, &l, 10); - if (rv != 0) - return rv; + return ret; +} - if (l >= 0 && l <= UINT32_MAX) { - *n = (uint32_t) l; - return 0; - } +char * +get_host_name(char *word, char **host) +{ + char *delimiter = NULL; + delimiter = strrchr(word, ':'); + if (delimiter) + *delimiter = '\0'; + else + return NULL; + *host = word; + return *host; +} - errno = ERANGE; +char * +get_path_name(char *word, char **path) +{ + char *delimiter = NULL; + delimiter = strchr(word, '/'); + if (!delimiter) + return NULL; + *path = delimiter; + return *path; +} + +void +gf_path_strip_trailing_slashes(char *path) +{ + int i = 0; + int len = 0; + + if (!path) + return; + + len = strlen(path); + for (i = len - 1; i > 0; i--) { + if (path[i] != '/') + break; + } + + if (i < (len - 1)) + path[i + 1] = '\0'; + + return; +} + +uint64_t +get_mem_size() +{ + uint64_t memsize = -1; + +#if defined GF_LINUX_HOST_OS || defined GF_SOLARIS_HOST_OS + + uint64_t page_size = 0; + uint64_t num_pages = 0; + + page_size = sysconf(_SC_PAGESIZE); + num_pages = sysconf(_SC_PHYS_PAGES); + + memsize = page_size * num_pages; +#endif + +#if defined GF_DARWIN_HOST_OS || defined __FreeBSD__ + + size_t len = sizeof(memsize); + int name[] = {CTL_HW, HW_PHYSMEM}; + + sysctl(name, 2, &memsize, &len, NULL, 0); +#endif + +#if defined __NetBSD__ + + size_t len = sizeof(memsize); + int name64[] = {CTL_HW, HW_PHYSMEM64}; + + sysctl(name64, 2, &memsize, &len, NULL, 0); + if (memsize == -1) + sysctl(name64, 2, &memsize, &len, NULL, 0); +#endif + return memsize; +} + +/* Strips all whitespace characters in a string and returns length of new string + * on success + */ +int +gf_strip_whitespace(char *str, int len) +{ + int i = 0; + int new_len = 0; + char *new_str = NULL; + + GF_ASSERT(str); + + new_str = GF_MALLOC(len + 1, gf_common_mt_char); + if (new_str == NULL) return -1; + + for (i = 0; i < len; i++) { + if (!isspace(str[i])) + new_str[new_len++] = str[i]; + } + new_str[new_len] = '\0'; + + if (new_len != len) { + snprintf(str, new_len + 1, "%s", new_str); + } + + GF_FREE(new_str); + return new_len; } int -gf_string2uint64_base10 (const char *str, uint64_t *n) +gf_canonicalize_path(char *path) { - unsigned long long l = 0ULL; - int rv = 0; + int ret = -1; + int path_len = 0; + int dir_path_len = 0; + char *tmppath = NULL; + char *dir = NULL; + char *tmpstr = NULL; - rv = _gf_string2ulonglong (str, &l, 10); - if (rv != 0) - return rv; + if (!path || *path != '/') + goto out; - if (l >= 0 && l <= UINT64_MAX) { - *n = (uint64_t) l; - return 0; + if (!strcmp(path, "/")) + return 0; + + tmppath = gf_strdup(path); + if (!tmppath) + goto out; + + /* Strip the extra slashes and return */ + bzero(path, strlen(path)); + path[0] = '/'; + dir = strtok_r(tmppath, "/", &tmpstr); + + while (dir) { + dir_path_len = strlen(dir); + memcpy((path + path_len + 1), dir, dir_path_len); + path_len += dir_path_len + 1; + dir = strtok_r(NULL, "/", &tmpstr); + if (dir) { + path[path_len] = '/'; } + } + path[path_len] = '\0'; + ret = 0; - errno = ERANGE; - return -1; +out: + if (ret) + gf_smsg("common-utils", GF_LOG_ERROR, 0, LG_MSG_PATH_ERROR, NULL); + + GF_FREE(tmppath); + + return ret; +} + +static const char *__gf_timefmts[] = { + "%F %T", "%Y/%m/%d-%T", "%b %d %T", "%F %H%M%S", "%Y-%m-%d-%T", "%s", +}; + +static const char *__gf_zerotimes[] = { + "0000-00-00 00:00:00", "0000/00/00-00:00:00", "xxx 00 00:00:00", + "0000-00-00 000000", "0000-00-00-00:00:00", "0", +}; + +void +_gf_timestuff(const char ***fmts, const char ***zeros) +{ + *fmts = __gf_timefmts; + *zeros = __gf_zerotimes; } char * -gf_uint64_2human_readable (uint64_t n) -{ - int ret = 0; - char *str = NULL; - - if (n >= GF_UNIT_PB) { - ret = gf_asprintf (&str, "%.1lfPB", ((double) n)/GF_UNIT_PB); - if (ret < 0) - goto err; - } else if (n >= GF_UNIT_TB) { - ret = gf_asprintf (&str, "%.1lfTB", ((double) n)/GF_UNIT_TB); - if (ret < 0) - goto err; - } else if (n >= GF_UNIT_GB) { - ret = gf_asprintf (&str, "%.1lfGB", ((double) n)/GF_UNIT_GB); - if (ret < 0) - goto err; - } else if (n >= GF_UNIT_MB) { - ret = gf_asprintf (&str, "%.1lfMB", ((double) n)/GF_UNIT_MB); - if (ret < 0) - goto err; - } else if (n >= GF_UNIT_KB) { - ret = gf_asprintf (&str, "%.1lfKB", ((double) n)/GF_UNIT_KB); - if (ret < 0) - goto err; - } else { - ret = gf_asprintf (&str, "%luBytes", n); - if (ret < 0) - goto err; - } - return str; -err: - return NULL; +generate_glusterfs_ctx_id(void) +{ + uuid_t ctxid; + char *tmp = NULL; + + gf_uuid_generate(ctxid); + tmp = uuid_utoa(ctxid); + + return gf_strdup(tmp); +} + +char * +gf_get_reserved_ports() +{ + char *ports_info = NULL; +#if defined GF_LINUX_HOST_OS + int proc_fd = -1; + char *proc_file = "/proc/sys/net/ipv4/ip_local_reserved_ports"; + char buffer[4096] = { + 0, + }; + int32_t ret = -1; + + proc_fd = open(proc_file, O_RDONLY); + if (proc_fd == -1) { + /* What should be done in this case? error out from here + * and thus stop the glusterfs process from starting or + * continue with older method of using any of the available + * port? For now 2nd option is considered. + */ + gf_smsg("glusterfs", GF_LOG_WARNING, errno, LG_MSG_FILE_OP_FAILED, + " /proc/sys/net/ipv4/ip_local_reserved_ports", NULL); + goto out; + } + + ret = sys_read(proc_fd, buffer, sizeof(buffer) - 1); + if (ret < 0) { + gf_smsg("glusterfs", GF_LOG_WARNING, errno, LG_MSG_FILE_OP_FAILED, + "file=%s", proc_file, NULL); + goto out; + } + + buffer[ret] = '\0'; + ports_info = gf_strdup(buffer); + +out: + if (proc_fd != -1) + sys_close(proc_fd); +#endif /* GF_LINUX_HOST_OS */ + return ports_info; } int -gf_string2bytesize (const char *str, uint64_t *n) +gf_process_reserved_ports(unsigned char *ports, uint32_t ceiling) { - uint64_t value = 0ULL; - char *tail = NULL; - int old_errno = 0; - const char *s = NULL; + int ret = -1; - if (str == NULL || n == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - errno = EINVAL; - return -1; - } + memset(ports, 0, GF_PORT_ARRAY_SIZE); - for (s = str; *s != '\0'; s++) { - if (isspace (*s)) - continue; - if (*s == '-') - return -1; - break; - } +#if defined GF_LINUX_HOST_OS + char *ports_info = NULL; + char *tmp = NULL; + char *blocked_port = NULL; - old_errno = errno; - errno = 0; - value = strtoull (str, &tail, 10); + ports_info = gf_get_reserved_ports(); + if (!ports_info) { + gf_smsg("glusterfs", GF_LOG_WARNING, 0, LG_MSG_RESERVED_PORTS_ERROR, + NULL); + goto out; + } - if (errno == ERANGE || errno == EINVAL) - return -1; + blocked_port = strtok_r(ports_info, ",\n", &tmp); - if (errno == 0) - errno = old_errno; + while (blocked_port) { + gf_ports_reserved(blocked_port, ports, ceiling); + blocked_port = strtok_r(NULL, ",\n", &tmp); + } - if (tail[0] != '\0') - { - if (strcasecmp (tail, GF_UNIT_KB_STRING) == 0) - value *= GF_UNIT_KB; - else if (strcasecmp (tail, GF_UNIT_MB_STRING) == 0) - value *= GF_UNIT_MB; - else if (strcasecmp (tail, GF_UNIT_GB_STRING) == 0) - value *= GF_UNIT_GB; - else if (strcasecmp (tail, GF_UNIT_TB_STRING) == 0) - value *= GF_UNIT_TB; - else if (strcasecmp (tail, GF_UNIT_PB_STRING) == 0) - value *= GF_UNIT_PB; - else - return -1; - } + ret = 0; - *n = value; +out: + GF_FREE(ports_info); - return 0; +#else /* FIXME: Non Linux Host */ + ret = 0; +#endif /* GF_LINUX_HOST_OS */ + + return ret; } -int64_t -gf_str_to_long_long (const char *number) +gf_boolean_t +gf_ports_reserved(char *blocked_port, unsigned char *ports, uint32_t ceiling) { - int64_t unit = 1; - int64_t ret = 0; - char *endptr = NULL ; - if (!number) - return 0; - - ret = strtoll (number, &endptr, 0); - - if (endptr) { - switch (*endptr) { - case 'G': - case 'g': - if ((* (endptr + 1) == 'B') ||(* (endptr + 1) == 'b')) - unit = 1024 * 1024 * 1024; - break; - case 'M': - case 'm': - if ((* (endptr + 1) == 'B') ||(* (endptr + 1) == 'b')) - unit = 1024 * 1024; - break; - case 'K': - case 'k': - if ((* (endptr + 1) == 'B') ||(* (endptr + 1) == 'b')) - unit = 1024; - break; - case '%': - unit = 1; - break; - default: - unit = 1; - break; - } + gf_boolean_t result = _gf_false; + char *range_port = NULL; + int32_t tmp_port1 = -1; + int32_t tmp_port2 = -1; + + if (strstr(blocked_port, "-") == NULL) { + /* get rid of the new line character*/ + if (blocked_port[strlen(blocked_port) - 1] == '\n') + blocked_port[strlen(blocked_port) - 1] = '\0'; + if (gf_string2int32(blocked_port, &tmp_port1) == 0) { + if (tmp_port1 > GF_PORT_MAX || tmp_port1 < 0) { + gf_smsg("glusterfs-socket", GF_LOG_WARNING, 0, + LG_MSG_INVALID_PORT, "port=%d", tmp_port1, NULL); + result = _gf_true; + goto out; + } else { + gf_msg_debug("glusterfs", 0, + "blocking port " + "%d", + tmp_port1); + BIT_SET(ports, tmp_port1); + } + } else { + gf_smsg("glusterfs-socket", GF_LOG_WARNING, 0, LG_MSG_INVALID_PORT, + "port=%s", blocked_port, NULL); + result = _gf_true; + goto out; } - return ret * unit; + } else { + range_port = strtok(blocked_port, "-"); + if (!range_port) { + result = _gf_true; + goto out; + } + if (gf_string2int32(range_port, &tmp_port1) == 0) { + if (tmp_port1 > ceiling) + tmp_port1 = ceiling; + if (tmp_port1 < 0) + tmp_port1 = 0; + } + range_port = strtok(NULL, "-"); + if (!range_port) { + result = _gf_true; + goto out; + } + /* get rid of the new line character*/ + if (range_port[strlen(range_port) - 1] == '\n') + range_port[strlen(range_port) - 1] = '\0'; + if (gf_string2int32(range_port, &tmp_port2) == 0) { + if (tmp_port2 > ceiling) + tmp_port2 = ceiling; + if (tmp_port2 < 0) + tmp_port2 = 0; + } + gf_msg_debug("glusterfs", 0, "lower: %d, higher: %d", tmp_port1, + tmp_port2); + for (; tmp_port1 <= tmp_port2; tmp_port1++) + BIT_SET(ports, tmp_port1); + } + +out: + return result; } +/* Takes in client ip{v4,v6} and returns associated hostname, if any + * Also, allocates memory for the hostname. + * Returns: 0 for success, -1 for failure + */ int -gf_string2boolean (const char *str, gf_boolean_t *b) +gf_get_hostname_from_ip(char *client_ip, char **hostname) { - if (str == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); - return -1; + int ret = -1; + struct sockaddr *client_sockaddr = NULL; + struct sockaddr_in client_sock_in = {0}; + struct sockaddr_in6 client_sock_in6 = {0}; + char client_hostname[NI_MAXHOST] = {0}; + char *client_ip_copy = NULL; + char *tmp = NULL; + char *ip = NULL; + size_t addr_sz = 0; + + /* if ipv4, reverse lookup the hostname to + * allow FQDN based rpc authentication + */ + if (!valid_ipv6_address(client_ip, strlen(client_ip), 0) && + !valid_ipv4_address(client_ip, strlen(client_ip), 0)) { + /* most times, we get a.b.c.d:port form, so check that */ + client_ip_copy = gf_strdup(client_ip); + if (!client_ip_copy) + goto out; + + ip = strtok_r(client_ip_copy, ":", &tmp); + } else { + ip = client_ip; + } + + if (valid_ipv4_address(ip, strlen(ip), 0) == _gf_true) { + client_sockaddr = (struct sockaddr *)&client_sock_in; + addr_sz = sizeof(client_sock_in); + client_sock_in.sin_family = AF_INET; + ret = inet_pton(AF_INET, ip, (void *)&client_sock_in.sin_addr.s_addr); + + } else if (valid_ipv6_address(ip, strlen(ip), 0) == _gf_true) { + client_sockaddr = (struct sockaddr *)&client_sock_in6; + addr_sz = sizeof(client_sock_in6); + + client_sock_in6.sin6_family = AF_INET6; + ret = inet_pton(AF_INET6, ip, (void *)&client_sock_in6.sin6_addr); + } else { + goto out; + } + + if (ret != 1) { + ret = -1; + goto out; + } + + /* You cannot just use sizeof (*client_sockaddr), as per the man page + * the (getnameinfo) size must be the size of the underlying sockaddr + * struct e.g. sockaddr_in6 or sockaddr_in. Failure to do so will + * break IPv6 hostname resolution (IPv4 will work only because + * the sockaddr_in struct happens to be of the correct size). + */ + ret = getnameinfo(client_sockaddr, addr_sz, client_hostname, + sizeof(client_hostname), NULL, 0, 0); + if (ret) { + gf_smsg("common-utils", GF_LOG_ERROR, 0, LG_MSG_GETNAMEINFO_FAILED, + "ip=%s", client_ip, "ret=%s", gai_strerror(ret), NULL); + ret = -1; + goto out; + } + + *hostname = gf_strdup((char *)client_hostname); +out: + if (client_ip_copy) + GF_FREE(client_ip_copy); + + return ret; +} + +gf_boolean_t +gf_interface_search(char *ip) +{ + int32_t ret = -1; + gf_boolean_t found = _gf_false; + struct ifaddrs *ifaddr, *ifa; + int family; + char host[NI_MAXHOST]; + xlator_t *this = NULL; + char *pct = NULL; + + this = THIS; + + ret = getifaddrs(&ifaddr); + + if (ret != 0) { + gf_smsg(this->name, GF_LOG_ERROR, 0, LG_MSG_GETIFADDRS_FAILED, "ret=%s", + gai_strerror(ret), NULL); + goto out; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) { + /* + * This seemingly happens if an interface hasn't + * been bound to a particular protocol (seen with + * TUN devices). + */ + continue; } + family = ifa->ifa_addr->sa_family; - if ((strcasecmp (str, "1") == 0) || - (strcasecmp (str, "on") == 0) || - (strcasecmp (str, "yes") == 0) || - (strcasecmp (str, "true") == 0) || - (strcasecmp (str, "enable") == 0)) { - *b = _gf_true; - return 0; + if (family != AF_INET && family != AF_INET6) + continue; + + ret = getnameinfo(ifa->ifa_addr, + (family == AF_INET) ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + + if (ret != 0) { + gf_smsg(this->name, GF_LOG_ERROR, 0, LG_MSG_GETNAMEINFO_FAILED, + "ret=%s", gai_strerror(ret), NULL); + goto out; } - if ((strcasecmp (str, "0") == 0) || - (strcasecmp (str, "off") == 0) || - (strcasecmp (str, "no") == 0) || - (strcasecmp (str, "false") == 0) || - (strcasecmp (str, "disable") == 0)) { - *b = _gf_false; - return 0; + /* + * Sometimes the address comes back as addr%eth0 or + * similar. Since % is an invalid character, we can + * strip it out with confidence that doing so won't + * harm anything. + */ + pct = index(host, '%'); + if (pct) { + *pct = '\0'; } - return -1; + if (strncmp(ip, host, NI_MAXHOST) == 0) { + gf_msg_debug(this->name, 0, + "%s is local address at " + "interface %s", + ip, ifa->ifa_name); + found = _gf_true; + goto out; + } + } +out: + if (ifaddr) + freeifaddrs(ifaddr); + return found; } +char * +get_ip_from_addrinfo(struct addrinfo *addr, char **ip) +{ + char buf[64]; + void *in_addr = NULL; + struct sockaddr_in *s4 = NULL; + struct sockaddr_in6 *s6 = NULL; + + switch (addr->ai_family) { + case AF_INET: + s4 = (struct sockaddr_in *)addr->ai_addr; + in_addr = &s4->sin_addr; + break; + + case AF_INET6: + s6 = (struct sockaddr_in6 *)addr->ai_addr; + in_addr = &s6->sin6_addr; + break; + + default: + gf_smsg("glusterd", GF_LOG_ERROR, 0, LG_MSG_INVALID_FAMILY, NULL); + return NULL; + } + + if (!inet_ntop(addr->ai_family, in_addr, buf, sizeof(buf))) { + gf_smsg("glusterd", GF_LOG_ERROR, 0, LG_MSG_CONVERSION_FAILED, NULL); + return NULL; + } -int -gf_lockfd (int fd) + *ip = gf_strdup(buf); + return *ip; +} + +gf_boolean_t +gf_is_loopback_localhost(const struct sockaddr *sa, char *hostname) +{ + GF_ASSERT(sa); + + gf_boolean_t is_local = _gf_false; + const struct in_addr *addr4 = NULL; + const struct in6_addr *addr6 = NULL; + uint8_t *ap = NULL; + struct in6_addr loopbackaddr6 = IN6ADDR_LOOPBACK_INIT; + + switch (sa->sa_family) { + case AF_INET: + addr4 = &(((struct sockaddr_in *)sa)->sin_addr); + ap = (uint8_t *)&addr4->s_addr; + if (ap[0] == 127) + is_local = _gf_true; + break; + + case AF_INET6: + addr6 = &(((struct sockaddr_in6 *)sa)->sin6_addr); + if (memcmp(addr6, &loopbackaddr6, sizeof(loopbackaddr6)) == 0) + is_local = _gf_true; + break; + + default: + if (hostname) + gf_smsg("glusterd", GF_LOG_ERROR, 0, LG_MSG_INVALID_FAMILY, + "family=%d", sa->sa_family, "hostname=%s", hostname, + NULL); + break; + } + + return is_local; +} + +gf_boolean_t +gf_is_local_addr(char *hostname) { - struct gf_flock fl; + int32_t ret = -1; + struct addrinfo *result = NULL; + struct addrinfo *res = NULL; + gf_boolean_t found = _gf_false; + char *ip = NULL; + xlator_t *this = NULL; + struct addrinfo hints; + + this = THIS; + + memset(&hints, 0, sizeof(hints)); + /* + * Removing AI_ADDRCONFIG from default_hints + * for being able to use link local ipv6 addresses + */ + hints.ai_family = AF_UNSPEC; + + ret = getaddrinfo(hostname, NULL, &hints, &result); + + if (ret != 0) { + gf_smsg(this->name, GF_LOG_ERROR, 0, LG_MSG_GETADDRINFO_FAILED, + "ret=%s", gai_strerror(ret), NULL); + goto out; + } + + for (res = result; res != NULL; res = res->ai_next) { + get_ip_from_addrinfo(res, &ip); + gf_msg_debug(this->name, 0, "%s ", ip); + + if (ip) { + found = (gf_is_loopback_localhost(res->ai_addr, hostname) || + gf_interface_search(ip)); + } + if (found) { + GF_FREE(ip); + goto out; + } + GF_FREE(ip); + /* the above free will not set ip to NULL, and hence, there is + double free possible as the loop continues. set ip to NULL. */ + ip = NULL; + } + +out: + if (result) + freeaddrinfo(result); - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; + if (!found) + gf_msg_debug(this->name, 0, "%s is not local", hostname); - return fcntl (fd, F_SETLK, &fl); + return found; } +gf_boolean_t +gf_is_same_address(char *name1, char *name2) +{ + struct addrinfo *addr1 = NULL; + struct addrinfo *addr2 = NULL; + struct addrinfo *p = NULL; + struct addrinfo *q = NULL; + gf_boolean_t ret = _gf_false; + int gai_err = 0; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + + gai_err = getaddrinfo(name1, NULL, &hints, &addr1); + if (gai_err != 0) { + gf_smsg(name1, GF_LOG_WARNING, 0, LG_MSG_GETADDRINFO_FAILED, "error=%s", + gai_strerror(gai_err), NULL); + goto out; + } + + gai_err = getaddrinfo(name2, NULL, &hints, &addr2); + if (gai_err != 0) { + gf_smsg(name2, GF_LOG_WARNING, 0, LG_MSG_GETADDRINFO_FAILED, "error=%s", + gai_strerror(gai_err), NULL); + goto out; + } + + for (p = addr1; p; p = p->ai_next) { + for (q = addr2; q; q = q->ai_next) { + if (p->ai_addrlen != q->ai_addrlen) { + continue; + } + if (memcmp(p->ai_addr, q->ai_addr, p->ai_addrlen)) { + continue; + } + ret = _gf_true; + goto out; + } + } + +out: + if (addr1) { + freeaddrinfo(addr1); + } + if (addr2) { + freeaddrinfo(addr2); + } + return ret; +} +/* + * Processes list of volfile servers. + * Format: <host1>:<port1> <host2>:<port2>... + */ int -gf_unlockfd (int fd) +gf_process_getspec_servers_list(cmd_args_t *cmd_args, const char *servers_list) { - struct gf_flock fl; + char *tmp = NULL; + char *address = NULL; + char *host = NULL; + char *last_colon = NULL; + char *save_ptr = NULL; + int port = 0; + int ret = -1; + + tmp = gf_strdup(servers_list); + if (!tmp) { + errno = ENOMEM; + goto out; + } + + address = strtok_r(tmp, " ", &save_ptr); + if (!address) { + errno = EINVAL; + goto out; + } + + while (1) { + last_colon = strrchr(address, ':'); + if (!last_colon) { + errno = EINVAL; + ret = -1; + break; + } + *last_colon = '\0'; + host = address; + port = atoi(last_colon + 1); + if (port <= 0) { + errno = EINVAL; + ret = -1; + break; + } + ret = gf_set_volfile_server_common(cmd_args, host, + GF_DEFAULT_VOLFILE_TRANSPORT, port); + if (ret && errno != EEXIST) { + break; + } + address = strtok_r(NULL, " ", &save_ptr); + if (!address) { + errno = 0; + ret = 0; + break; + } + } - fl.l_type = F_UNLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; +out: + if (tmp) { + GF_FREE(tmp); + } - return fcntl (fd, F_SETLK, &fl); + return ret; } -static void -compute_checksum (char *buf, size_t size, uint32_t *checksum) +int +gf_set_volfile_server_common(cmd_args_t *cmd_args, const char *host, + const char *transport, int port) { - int ret = -1; - char *checksum_buf = NULL; + server_cmdline_t *server = NULL; + server_cmdline_t *tmp = NULL; + int ret = -1; + + GF_VALIDATE_OR_GOTO(THIS->name, cmd_args, out); + GF_VALIDATE_OR_GOTO(THIS->name, host, out); + GF_VALIDATE_OR_GOTO(THIS->name, transport, out); + + server = GF_CALLOC(1, sizeof(server_cmdline_t), + gf_common_mt_server_cmdline_t); + if (!server) { + errno = ENOMEM; + goto out; + } + + INIT_LIST_HEAD(&server->list); + + server->volfile_server = gf_strdup(host); + if (!server->volfile_server) { + errno = ENOMEM; + goto out; + } + + server->transport = gf_strdup(transport); + if (!server->transport) { + errno = ENOMEM; + goto out; + } + + server->port = port; + + if (!cmd_args->volfile_server) { + cmd_args->volfile_server = server->volfile_server; + cmd_args->volfile_server_transport = server->transport; + cmd_args->volfile_server_port = server->port; + cmd_args->curr_server = server; + } + + list_for_each_entry(tmp, &cmd_args->volfile_servers, list) + { + if ((!strcmp(tmp->volfile_server, server->volfile_server) && + !strcmp(tmp->transport, server->transport) && + (tmp->port == server->port))) { + /* Duplicate option given, log and ignore */ + gf_smsg("gluster", GF_LOG_INFO, EEXIST, LG_MSG_DUPLICATE_ENTRY, + NULL); + ret = 0; + goto out; + } + } - checksum_buf = (char *)(checksum); + list_add_tail(&server->list, &cmd_args->volfile_servers); - if (!(*checksum)) { - checksum_buf [0] = 0xba; - checksum_buf [1] = 0xbe; - checksum_buf [2] = 0xb0; - checksum_buf [3] = 0x0b; + ret = 0; +out: + if (-1 == ret) { + if (server) { + GF_FREE(server->volfile_server); + GF_FREE(server->transport); + GF_FREE(server); } + } - for (ret = 0; ret < (size - 4); ret += 4) { - checksum_buf[0] ^= (buf[ret]); - checksum_buf[1] ^= (buf[ret + 1] << 1) ; - checksum_buf[2] ^= (buf[ret + 2] << 2); - checksum_buf[3] ^= (buf[ret + 3] << 3); + return ret; +} + +/* Sets log file path from user provided arguments */ +int +gf_set_log_file_path(cmd_args_t *cmd_args, glusterfs_ctx_t *ctx) +{ + int i = 0; + int j = 0; + int ret = 0; + int tmp_len = 0; + char tmp_str[1024] = { + 0, + }; + + if (!cmd_args) + goto done; + + if (cmd_args->mount_point) { + j = 0; + i = 0; + if (cmd_args->mount_point[0] == '/') + i = 1; + for (; i < strlen(cmd_args->mount_point); i++, j++) { + tmp_str[j] = cmd_args->mount_point[i]; + if (cmd_args->mount_point[i] == '/') + tmp_str[j] = '-'; + } + + ret = gf_asprintf(&cmd_args->log_file, + DEFAULT_LOG_FILE_DIRECTORY "/%s.log", tmp_str); + if (ret > 0) + ret = 0; + goto done; + } + + if (ctx && GF_GLUSTERD_PROCESS == ctx->process_mode) { + ret = gf_asprintf(&cmd_args->log_file, + DEFAULT_LOG_FILE_DIRECTORY "/%s.log", GLUSTERD_NAME); + if (ret > 0) + ret = 0; + + goto done; + } + + if (cmd_args->volfile) { + j = 0; + i = 0; + if (cmd_args->volfile[0] == '/') + i = 1; + for (; i < strlen(cmd_args->volfile); i++, j++) { + tmp_str[j] = cmd_args->volfile[i]; + if (cmd_args->volfile[i] == '/') + tmp_str[j] = '-'; + } + ret = gf_asprintf(&cmd_args->log_file, + DEFAULT_LOG_FILE_DIRECTORY "/%s.log", tmp_str); + if (ret > 0) + ret = 0; + goto done; + } + + if (cmd_args->volfile_server) { + if (strncmp(cmd_args->volfile_server_transport, "unix", 4) == 0) { + if (cmd_args->volfile_server[0] == '/') + i = 1; + tmp_len = strlen(cmd_args->volfile_server); + for (j = 0; i < tmp_len; i++, j++) { + tmp_str[j] = cmd_args->volfile_server[i]; + if (cmd_args->volfile_server[i] == '/') + tmp_str[j] = '-'; + } + ret = gf_asprintf(&cmd_args->log_file, "%s/%s-%s-%d.log", + DEFAULT_LOG_FILE_DIRECTORY, tmp_str, + cmd_args->volfile_id, getpid()); + } else { + ret = gf_asprintf(&cmd_args->log_file, "%s/%s-%s-%d.log", + DEFAULT_LOG_FILE_DIRECTORY, + cmd_args->volfile_server, cmd_args->volfile_id, + getpid()); } + if (ret > 0) + ret = 0; + } +done: + return ret; +} - for (ret = 0; ret <= (size % 4); ret++) { - checksum_buf[ret] ^= (buf[(size - 4) + ret] << ret); +int +gf_set_log_ident(cmd_args_t *cmd_args) +{ + int ret = 0; + char *ptr = NULL; + + if (cmd_args->log_file == NULL) { + /* no ident source */ + return 0; + } + + /* TODO: Some idents would look like, etc-glusterfs-glusterd.vol, which + * seems ugly and can be bettered? */ + /* just get the filename as the ident */ + if (NULL != (ptr = strrchr(cmd_args->log_file, '/'))) { + ret = gf_asprintf(&cmd_args->log_ident, "%s", ptr + 1); + } else { + ret = gf_asprintf(&cmd_args->log_ident, "%s", cmd_args->log_file); + } + + if (ret > 0) + ret = 0; + else + return ret; + + /* remove .log suffix */ + if (NULL != (ptr = strrchr(cmd_args->log_ident, '.'))) { + if (strcmp(ptr, ".log") == 0) { + ptr[0] = '\0'; } + } + + return ret; +} + +int +gf_thread_cleanup_xint(pthread_t thread) +{ + int ret = 0; + void *res = NULL; + + ret = pthread_cancel(thread); + if (ret != 0) + goto error_return; + + ret = pthread_join(thread, &res); + if (ret != 0) + goto error_return; + + if (res != PTHREAD_CANCELED) + goto error_return; + + ret = 0; + +error_return: + return ret; +} +void +gf_thread_set_vname(pthread_t thread, const char *name, va_list args) +{ + char thread_name[GF_THREAD_NAME_LIMIT]; + int ret; + + /* Initialize the thread name with the prefix (not NULL terminated). */ + memcpy(thread_name, GF_THREAD_NAME_PREFIX, + sizeof(GF_THREAD_NAME_PREFIX) - 1); + + ret = vsnprintf(thread_name + sizeof(GF_THREAD_NAME_PREFIX) - 1, + sizeof(thread_name) - sizeof(GF_THREAD_NAME_PREFIX) + 1, + name, args); + if (ret < 0) { + gf_smsg(THIS->name, GF_LOG_WARNING, 0, LG_MSG_PTHREAD_NAMING_FAILED, + "name=%s", name, NULL); return; + } + + if (ret >= sizeof(thread_name)) { + gf_smsg(THIS->name, GF_LOG_WARNING, 0, LG_MSG_THREAD_NAME_TOO_LONG, + "name=%s", thread_name, NULL); + } + +#ifdef GF_LINUX_HOST_OS + ret = pthread_setname_np(thread, thread_name); +#elif defined(__NetBSD__) + ret = pthread_setname_np(thread, thread_name, NULL); +#elif defined(__FreeBSD__) + pthread_set_name_np(thread, thread_name); + ret = 0; +#else + ret = ENOSYS; +#endif + if (ret != 0) { + gf_smsg(THIS->name, GF_LOG_WARNING, ret, LG_MSG_SET_THREAD_FAILED, + "name=%s", thread_name, NULL); + } } -#define GF_CHECKSUM_BUF_SIZE 1024 +void +gf_thread_set_name(pthread_t thread, const char *name, ...) +{ + va_list args; + + va_start(args, name); + gf_thread_set_vname(thread, name, args); + va_end(args); +} int -get_checksum_for_file (int fd, uint32_t *checksum) +gf_thread_vcreate(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg, const char *name, + va_list args) { - int ret = -1; - char buf[GF_CHECKSUM_BUF_SIZE] = {0,}; + sigset_t set, old; + int ret; + + sigemptyset(&old); + sigfillset(&set); + sigdelset(&set, SIGSEGV); + sigdelset(&set, SIGBUS); + sigdelset(&set, SIGILL); + sigdelset(&set, SIGSYS); + sigdelset(&set, SIGFPE); + sigdelset(&set, SIGABRT); + + pthread_sigmask(SIG_BLOCK, &set, &old); + + ret = pthread_create(thread, attr, start_routine, arg); + if (ret != 0) { + gf_smsg(THIS->name, GF_LOG_ERROR, ret, LG_MSG_THREAD_CREATE_FAILED, + NULL); + ret = -1; + } else if (name != NULL) { + gf_thread_set_vname(*thread, name, args); + } + + pthread_sigmask(SIG_SETMASK, &old, NULL); + + return ret; +} - /* goto first place */ - lseek (fd, 0L, SEEK_SET); - do { - ret = read (fd, &buf, GF_CHECKSUM_BUF_SIZE); - if (ret > 0) - compute_checksum (buf, GF_CHECKSUM_BUF_SIZE, - checksum); - } while (ret > 0); +int +gf_thread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg, const char *name, + ...) +{ + va_list args; + int ret; - /* set it back */ - lseek (fd, 0L, SEEK_SET); + va_start(args, name); + ret = gf_thread_vcreate(thread, attr, start_routine, arg, name, args); + va_end(args); - return ret; + return ret; } +int +gf_thread_create_detached(pthread_t *thread, void *(*start_routine)(void *), + void *arg, const char *name, ...) +{ + pthread_attr_t attr; + va_list args; + int ret = -1; + + ret = pthread_attr_init(&attr); + if (ret) { + gf_smsg(THIS->name, GF_LOG_ERROR, ret, LG_MSG_PTHREAD_ATTR_INIT_FAILED, + NULL); + return -1; + } + + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + va_start(args, name); + ret = gf_thread_vcreate(thread, &attr, start_routine, arg, name, args); + va_end(args); + + pthread_attr_destroy(&attr); + + return ret; +} int -get_checksum_for_path (char *path, uint32_t *checksum) +gf_skip_header_section(int fd, int header_len) { - int ret = -1; - int fd = -1; + int ret = -1; - GF_ASSERT (path); - GF_ASSERT (checksum); + ret = sys_lseek(fd, header_len, SEEK_SET); + if (ret == (off_t)-1) { + gf_smsg("", GF_LOG_ERROR, 0, LG_MSG_SKIP_HEADER_FAILED, NULL); + } else { + ret = 0; + } - fd = open (path, O_RDWR); + return ret; +} - if (fd == -1) { - gf_log (THIS->name, GF_LOG_ERROR, "Unable to open %s, errno: %d", - path, errno); - goto out; - } +/* Below function is use to check at runtime if pid is running */ - ret = get_checksum_for_file (fd, checksum); +gf_boolean_t +gf_is_pid_running(int pid) +{ +#ifdef __FreeBSD__ + int ret = -1; + + ret = sys_kill(pid, 0); + if (ret < 0) { + return _gf_false; + } +#else + char fname[32] = { + 0, + }; + int fd = -1; + + snprintf(fname, sizeof(fname), "/proc/%d/cmdline", pid); + + fd = sys_open(fname, O_RDONLY, 0); + if (fd < 0) { + return _gf_false; + } + + sys_close(fd); +#endif + return _gf_true; +} +gf_boolean_t +gf_is_service_running(char *pidfile, int *pid) +{ + FILE *file = NULL; + gf_boolean_t running = _gf_false; + int ret = 0; + int fno = 0; + + file = fopen(pidfile, "r+"); + if (!file) { + goto out; + } + + fno = fileno(file); + ret = lockf(fno, F_TEST, 0); + if (ret == -1) { + running = _gf_true; + } + + ret = fscanf(file, "%d", pid); + if (ret <= 0) { + gf_smsg("", GF_LOG_ERROR, errno, LG_MSG_FILE_OP_FAILED, "pidfile=%s", + pidfile, NULL); + *pid = -1; + running = _gf_false; + goto out; + } + + running = gf_is_pid_running(*pid); out: - if (fd != -1) - close (fd); + if (file) + fclose(file); + return running; +} - return ret; +/* Check if the pid is > 0 */ +gf_boolean_t +gf_valid_pid(const char *pid, int length) +{ + gf_boolean_t ret = _gf_true; + pid_t value = 0; + char *end_ptr = NULL; + + if (length <= 0) { + ret = _gf_false; + goto out; + } + + value = strtol(pid, &end_ptr, 10); + if (value <= 0) { + ret = _gf_false; + } +out: + return ret; } -char * -strtail (char *str, const char *pattern) +static int +dht_is_linkfile_key(dict_t *this, char *key, data_t *value, void *data) { - int i = 0; + gf_boolean_t *linkfile_key_found = NULL; - for (i = 0; str[i] == pattern[i] && str[i]; i++); + if (!data) + goto out; - if (pattern[i] == '\0') - return str + i; + linkfile_key_found = data; - return NULL; + *linkfile_key_found = _gf_true; +out: + return 0; } -void -skipwhite (char **s) +gf_boolean_t +dht_is_linkfile(struct iatt *buf, dict_t *dict) { - while (isspace (**s)) - (*s)++; + gf_boolean_t linkfile_key_found = _gf_false; + + if (!IS_DHT_LINKFILE_MODE(buf)) + return _gf_false; + + dict_foreach_fnmatch(dict, "*." DHT_LINKFILE_STR, dht_is_linkfile_key, + &linkfile_key_found); + + return linkfile_key_found; } -char * -nwstrtail (char *str, char *pattern) +int +gf_check_log_format(const char *value) { - for (;;) { - skipwhite (&str); - skipwhite (&pattern); + int log_format = -1; - if (*str != *pattern || !*str) - break; + if (!strcasecmp(value, GF_LOG_FORMAT_NO_MSG_ID)) + log_format = gf_logformat_traditional; + else if (!strcasecmp(value, GF_LOG_FORMAT_WITH_MSG_ID)) + log_format = gf_logformat_withmsgid; - str++; - pattern++; - } + if (log_format == -1) + gf_smsg(THIS->name, GF_LOG_ERROR, 0, LG_MSG_INVALID_LOG, + "possible_values=" GF_LOG_FORMAT_NO_MSG_ID + "|" GF_LOG_FORMAT_WITH_MSG_ID, + NULL); - return *pattern ? NULL : str; + return log_format; } -void -skipword (char **s) +int +gf_check_logger(const char *value) { - if (!*s) - return; + int logger = -1; + + if (!strcasecmp(value, GF_LOGGER_GLUSTER_LOG)) + logger = gf_logger_glusterlog; + else if (!strcasecmp(value, GF_LOGGER_SYSLOG)) + logger = gf_logger_syslog; - skipwhite (s); + if (logger == -1) + gf_smsg(THIS->name, GF_LOG_ERROR, 0, LG_MSG_INVALID_LOG, + "possible_values=" GF_LOGGER_GLUSTER_LOG "|" GF_LOGGER_SYSLOG, + NULL); - while (!isspace(**s)) - (*s)++; + return logger; } -char * -get_nth_word (const char *str, int n) +/* gf_compare_sockaddr compares the given addresses @addr1 and @addr2 for + * equality, ie. if they both refer to the same address. + * + * This was inspired by sock_addr_cmp_addr() from + * https://www.opensource.apple.com/source/postfix/postfix-197/postfix/src/util/sock_addr.c + */ +gf_boolean_t +gf_compare_sockaddr(const struct sockaddr *addr1, const struct sockaddr *addr2) { - char buf[4096] = {0}; - char *start = NULL; - char *word = NULL; - int i = 0; - int word_len = 0; - const char *end = NULL; + GF_ASSERT(addr1 != NULL); + GF_ASSERT(addr2 != NULL); + + /* Obviously, the addresses don't match if their families are different + */ + if (addr1->sa_family != addr2->sa_family) + return _gf_false; + + if (AF_INET == addr1->sa_family) { + if (((struct sockaddr_in *)addr1)->sin_addr.s_addr == + ((struct sockaddr_in *)addr2)->sin_addr.s_addr) + return _gf_true; + + } else if (AF_INET6 == addr1->sa_family) { + if (memcmp((char *)&((struct sockaddr_in6 *)addr1)->sin6_addr, + (char *)&((struct sockaddr_in6 *)addr2)->sin6_addr, + sizeof(struct in6_addr)) == 0) + return _gf_true; + } + return _gf_false; +} - if (!str) - goto out; +/* + * gf_set_timestamp: + * It sets the mtime and atime of 'dest' file as of 'src'. + */ - snprintf (buf, sizeof (buf), "%s", str); - start = buf; +int +gf_set_timestamp(const char *src, const char *dest) +{ + struct stat sb = { + 0, + }; +#if defined(HAVE_UTIMENSAT) + struct timespec new_time[2] = {{ + 0, + }, + { + 0, + }}; +#else + struct timeval new_time[2] = {{ + 0, + }, + { + 0, + }}; +#endif + int ret = 0; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_ASSERT(src); + GF_ASSERT(dest); + + ret = sys_stat(src, &sb); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, errno, LG_MSG_FILE_STAT_FAILED, + "stat=%s", src, NULL); + goto out; + } + /* The granularity is nano seconds if `utimensat()` is available, + * and micro seconds otherwise. + */ +#if defined(HAVE_UTIMENSAT) + new_time[0].tv_sec = sb.st_atime; + new_time[0].tv_nsec = ST_ATIM_NSEC(&sb); + + new_time[1].tv_sec = sb.st_mtime; + new_time[1].tv_nsec = ST_MTIM_NSEC(&sb); + + /* dirfd = 0 is ignored because `dest` is an absolute path. */ + ret = sys_utimensat(AT_FDCWD, dest, new_time, AT_SYMLINK_NOFOLLOW); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, errno, LG_MSG_UTIMENSAT_FAILED, + "dest=%s", dest, NULL); + } +#else + new_time[0].tv_sec = sb.st_atime; + new_time[0].tv_usec = ST_ATIM_NSEC(&sb) / 1000; + + new_time[1].tv_sec = sb.st_mtime; + new_time[1].tv_usec = ST_MTIM_NSEC(&sb) / 1000; + + ret = sys_utimes(dest, new_time); + if (ret) { + gf_smsg(this->name, GF_LOG_ERROR, errno, LG_MSG_UTIMES_FAILED, + "dest=%s", dest, NULL); + } +#endif +out: + return ret; +} - for (i = 0; i < n-1; i++) - skipword (&start); +static void +gf_backtrace_end(char *buf, size_t frames) +{ + size_t pos = 0; - skipwhite (&start); - end = strpbrk ((const char *)start, " \t\n\0"); + if (!buf) + return; - if (!end) - goto out; + pos = strlen(buf); - word_len = abs (end - start); + frames = min(frames, GF_BACKTRACE_LEN - pos - 1); - word = GF_CALLOC (1, word_len + 1, gf_common_mt_strdup); - if (!word) - goto out; + if (0 == frames) + return; - strncpy (word, start, word_len); - *(word + word_len) = '\0'; - out: - return word; + memset(buf + pos, ')', frames); + buf[pos + frames] = '\0'; } -/* RFC 1123 & 952 */ -char -valid_host_name (char *address, int length) +/*Returns bytes written*/ +static int +gf_backtrace_append(char *buf, size_t pos, char *framestr) { - int i = 0; - char ret = 1; + if (pos >= GF_BACKTRACE_LEN) + return -1; + return snprintf(buf + pos, GF_BACKTRACE_LEN - pos, "(--> %s ", framestr); +} - if ((length > 75) || (length == 1)) { - ret = 0; - goto out; - } +static int +gf_backtrace_fillframes(char *buf) +{ + void *array[GF_BACKTRACE_FRAME_COUNT]; + size_t frames = 0; + FILE *fp = NULL; + char callingfn[GF_BACKTRACE_FRAME_COUNT - 2][1024] = { + {0}, + }; + int ret = -1; + int fd = -1; + size_t idx = 0; + size_t pos = 0; + size_t inc = 0; + char tmpl[] = "/tmp/glfs-bt-XXXXXX"; + + frames = backtrace(array, GF_BACKTRACE_FRAME_COUNT); + if (!frames) + return -1; - if (!isalnum (address[length - 1])) { - ret = 0; - goto out; + /* coverity[secure_temp] mkstemp uses 0600 as the mode and is safe */ + fd = mkstemp(tmpl); + if (fd == -1) + return -1; + + /* Calling unlink so that when the file is closed or program + * terminates the temporary file is deleted. + */ + ret = sys_unlink(tmpl); + if (ret < 0) { + gf_smsg(THIS->name, GF_LOG_INFO, 0, LG_MSG_FILE_DELETE_FAILED, + "temporary_file=%s", tmpl, NULL); + } + + /*The most recent two frames are the calling function and + * gf_backtrace_save, which we can infer.*/ + + backtrace_symbols_fd(&array[2], frames - 2, fd); + + fp = fdopen(fd, "r"); + if (!fp) { + sys_close(fd); + goto out; + } + + ret = fseek(fp, 0L, SEEK_SET); + if (ret) + goto out; + + pos = 0; + for (idx = 0; idx < frames - 2; idx++) { + ret = fscanf(fp, "%1023s", callingfn[idx]); + if (ret == EOF) + break; + inc = gf_backtrace_append(buf, pos, callingfn[idx]); + if (inc == -1) + break; + pos += inc; + } + gf_backtrace_end(buf, idx); + +out: + if (fp) + fclose(fp); + + return (idx > 0) ? 0 : -1; +} + +/* Optionally takes @buf to save backtrace. If @buf is NULL, uses the + * pre-allocated ctx->btbuf to avoid allocating memory while printing + * backtrace. + * TODO: This API doesn't provide flexibility in terms of no. of frames + * of the backtrace is being saved in the buffer. Deferring fixing it + * when there is a real-use for that.*/ + +char * +gf_backtrace_save(char *buf) +{ + char *bt = NULL; + + if (!buf) { + bt = THIS->ctx->btbuf; + GF_ASSERT(bt); + + } else { + bt = buf; + } + + if ((0 == gf_backtrace_fillframes(bt))) + return bt; + + gf_smsg(THIS->name, GF_LOG_WARNING, 0, LG_MSG_BACKTRACE_SAVE_FAILED, NULL); + return NULL; +} + +gf_loglevel_t +fop_log_level(glusterfs_fop_t fop, int op_errno) +{ + /* if gfid doesn't exist ESTALE comes */ + if (op_errno == ENOENT || op_errno == ESTALE) + return GF_LOG_DEBUG; + + if ((fop == GF_FOP_ENTRYLK) || (fop == GF_FOP_FENTRYLK) || + (fop == GF_FOP_FINODELK) || (fop == GF_FOP_INODELK) || + (fop == GF_FOP_LK)) { + /* + * if non-blocking lock fails EAGAIN comes + * if locks xlator is not loaded ENOSYS comes + */ + if (op_errno == EAGAIN || op_errno == ENOSYS) + return GF_LOG_DEBUG; + } + + if ((fop == GF_FOP_GETXATTR) || (fop == GF_FOP_FGETXATTR)) { + if (op_errno == ENOTSUP || op_errno == ENODATA) + return GF_LOG_DEBUG; + } + + if ((fop == GF_FOP_SETXATTR) || (fop == GF_FOP_FSETXATTR) || + (fop == GF_FOP_REMOVEXATTR) || (fop == GF_FOP_FREMOVEXATTR)) { + if (op_errno == ENOTSUP) + return GF_LOG_DEBUG; + } + + if (fop == GF_FOP_MKNOD || fop == GF_FOP_MKDIR) + if (op_errno == EEXIST) + return GF_LOG_DEBUG; + + if (fop == GF_FOP_SEEK) { +#ifdef HAVE_SEEK_HOLE + if (op_errno == ENXIO) { + return GF_LOG_DEBUG; } +#else + return GF_LOG_DEBUG; +#endif + } + + return GF_LOG_ERROR; +} - for (i = 0; i < length; i++) { - if (!isalnum (address[i]) && (address[i] != '.') - && (address[i] != '-')) { - ret = 0; - goto out; - } +/* This function will build absolute path of file/directory from the + * current location and relative path given from the current location + * For example consider our current path is /a/b/c/ and relative path + * from current location is ./../x/y/z .After parsing through this + * function the absolute path becomes /a/b/x/y/z/. + * + * The function gives a pointer to absolute path if it is successful + * and also returns zero. + * Otherwise function gives NULL pointer with returning an err value. + * + * So the user need to free memory allocated for path. + * + */ + +int32_t +gf_build_absolute_path(char *current_path, char *relative_path, char **path) +{ + char *absolute_path = NULL; + char *token = NULL; + char *component = NULL; + char *saveptr = NULL; + char *end = NULL; + int ret = 0; + size_t relativepath_len = 0; + size_t currentpath_len = 0; + size_t max_absolutepath_len = 0; + + GF_ASSERT(current_path); + GF_ASSERT(relative_path); + GF_ASSERT(path); + + if (!path || !current_path || !relative_path) { + ret = -EFAULT; + goto err; + } + /* Check for current and relative path + * current path should be absolute one and start from '/' + * relative path should not start from '/' + */ + currentpath_len = strlen(current_path); + if (current_path[0] != '/' || (currentpath_len > PATH_MAX)) { + gf_smsg(THIS->name, GF_LOG_ERROR, 0, LG_MSG_WRONG_VALUE, + "current-path=%s", current_path, NULL); + ret = -EINVAL; + goto err; + } + + relativepath_len = strlen(relative_path); + if (relative_path[0] == '/' || (relativepath_len > PATH_MAX)) { + gf_smsg(THIS->name, GF_LOG_ERROR, 0, LG_MSG_WRONG_VALUE, + "relative-path=%s", relative_path, NULL); + ret = -EINVAL; + goto err; + } + + /* It is maximum possible value for absolute path */ + max_absolutepath_len = currentpath_len + relativepath_len + 2; + + absolute_path = GF_CALLOC(1, max_absolutepath_len, gf_common_mt_char); + if (!absolute_path) { + ret = -ENOMEM; + goto err; + } + absolute_path[0] = '\0'; + + /* If current path is root i.e contains only "/", we do not + * need to copy it + */ + if (strcmp(current_path, "/") != 0) { + strcpy(absolute_path, current_path); + + /* We trim '/' at the end for easier string manipulation */ + gf_path_strip_trailing_slashes(absolute_path); + } + + /* Used to spilt relative path based on '/' */ + component = gf_strdup(relative_path); + if (!component) { + ret = -ENOMEM; + goto err; + } + + /* In the relative path, we want to consider ".." and "." + * if token is ".." , we just need to reduce one level hierarchy + * if token is "." , we just ignore it + * if token is NULL , end of relative path + * if absolute path becomes '\0' and still "..", then it is a bad + * relative path, it points to out of boundary area and stop + * building the absolute path + * All other cases we just concatenate token to the absolute path + */ + for (token = strtok_r(component, "/", &saveptr), + end = strchr(absolute_path, '\0'); + token; token = strtok_r(NULL, "/", &saveptr)) { + if (strcmp(token, ".") == 0) + continue; + + else if (strcmp(token, "..") == 0) { + if (absolute_path[0] == '\0') { + ret = -EACCES; + goto err; + } + + end = strrchr(absolute_path, '/'); + *end = '\0'; + } else { + ret = snprintf(end, max_absolutepath_len - strlen(absolute_path), + "/%s", token); + end = strchr(absolute_path, '\0'); } + } -out: - return ret; + if (strlen(absolute_path) > PATH_MAX) { + ret = -EINVAL; + goto err; + } + *path = gf_strdup(absolute_path); + +err: + if (component) + GF_FREE(component); + if (absolute_path) + GF_FREE(absolute_path); + return ret; } -char -valid_ipv4_address (char *address, int length) -{ - int octets = 0; - int value = 0; - char *tmp = NULL, *ptr = NULL, *prev = NULL, *endptr = NULL; - char ret = 1; - - tmp = gf_strdup (address); - prev = tmp; - prev = strtok_r (tmp, ".", &ptr); - - while (prev != NULL) { - octets++; - value = strtol (prev, &endptr, 10); - if ((value > 255) || (value < 0) || (endptr != NULL)) { - ret = 0; - goto out; - } - prev = strtok_r (NULL, ".", &ptr); +/* This is an utility function which will recursively delete + * a folder and its contents. + * + * @param delete_path folder to be deleted. + * + * @return 0 on success and -1 on failure. + */ +int +recursive_rmdir(const char *delete_path) +{ + int ret = -1; + char path[PATH_MAX] = { + 0, + }; + struct stat st = { + 0, + }; + DIR *dir = NULL; + struct dirent *entry = NULL; + struct dirent scratch[2] = { + { + 0, + }, + }; + xlator_t *this = NULL; + + this = THIS; + GF_ASSERT(this); + GF_VALIDATE_OR_GOTO(this->name, delete_path, out); + + dir = sys_opendir(delete_path); + if (!dir) { + gf_msg_debug(this->name, 0, + "Failed to open directory %s. " + "Reason : %s", + delete_path, strerror(errno)); + ret = 0; + goto out; + } + + while ((entry = sys_readdir(dir, scratch))) { + if (gf_irrelevant_entry(entry)) + continue; + snprintf(path, PATH_MAX, "%s/%s", delete_path, entry->d_name); + ret = sys_lstat(path, &st); + if (ret == -1) { + gf_msg_debug(this->name, 0, + "Failed to stat entry %s :" + " %s", + path, strerror(errno)); + (void)sys_closedir(dir); + goto out; } - if (octets != 4) { - ret = 0; + if (S_ISDIR(st.st_mode)) + ret = recursive_rmdir(path); + else + ret = sys_unlink(path); + + if (ret) { + gf_msg_debug(this->name, 0, + " Failed to remove %s. " + "Reason : %s", + path, strerror(errno)); } + gf_msg_debug(this->name, 0, "%s %s", + ret ? "Failed to remove" : "Removed", entry->d_name); + } + + ret = sys_closedir(dir); + if (ret) { + gf_msg_debug(this->name, 0, + "Failed to close dir %s. Reason :" + " %s", + delete_path, strerror(errno)); + } + + ret = sys_rmdir(delete_path); + if (ret) { + gf_msg_debug(this->name, 0, "Failed to rmdir: %s,err: %s", delete_path, + strerror(errno)); + } + out: - GF_FREE (tmp); - return ret; + return ret; +} +/* + * Input: Array of strings 'array' terminating in NULL + * string 'elem' to be searched in the array + * + * Output: Index of the element in the array if found, '-1' otherwise + */ +int +gf_get_index_by_elem(char **array, char *elem) +{ + int i = 0; + + for (i = 0; array[i]; i++) { + if (strcmp(elem, array[i]) == 0) + return i; + } + + return -1; } -char -valid_ipv6_address (char *address, int length) -{ - int hex_numbers = 0; - int value = 0; - char *tmp = NULL, *ptr = NULL, *prev = NULL, *endptr = NULL; - char ret = 1; - - tmp = gf_strdup (address); - prev = strtok_r (tmp, ":", &ptr); - - while (prev != NULL) { - hex_numbers++; - value = strtol (prev, &endptr, 16); - if ((value > 0xffff) || (value < 0) - || (endptr != NULL && *endptr != '\0')) { - ret = 0; - goto out; - } - prev = strtok_r (NULL, ":", &ptr); - } +static int +get_pathinfo_host(char *pathinfo, char *hostname, size_t size) +{ + char *start = NULL; + char *end = NULL; + int ret = -1; + int i = 0; + + if (!pathinfo) + goto out; + + start = strchr(pathinfo, ':'); + if (!start) + goto out; + + end = strrchr(pathinfo, ':'); + if (start == end) + goto out; + + memset(hostname, 0, size); + i = 0; + while (++start != end) + hostname[i++] = *start; + ret = 0; +out: + return ret; +} + +/*Note: 'pathinfo' should be gathered only from one brick*/ +int +glusterfs_is_local_pathinfo(char *pathinfo, gf_boolean_t *is_local) +{ + int ret = 0; + char pathinfohost[1024] = {0}; + char localhost[1024] = {0}; + + *is_local = _gf_false; + ret = get_pathinfo_host(pathinfo, pathinfohost, sizeof(pathinfohost)); + if (ret) + goto out; + + ret = gethostname(localhost, sizeof(localhost)); + if (ret) + goto out; + + if (!strcmp(localhost, pathinfohost)) + *is_local = _gf_true; +out: + return ret; +} - if (hex_numbers > 8) { +ssize_t +gf_nread(int fd, void *buf, size_t count) +{ + ssize_t ret = 0; + ssize_t read_bytes = 0; + + for (read_bytes = 0; read_bytes < count; read_bytes += ret) { + ret = sys_read(fd, buf + read_bytes, count - read_bytes); + if (ret == 0) { + break; + } else if (ret < 0) { + if (errno == EINTR) ret = 0; + else + goto out; } + } + ret = read_bytes; out: - GF_FREE (tmp); - return ret; + return ret; } -char -valid_internet_address (char *address) +ssize_t +gf_nwrite(int fd, const void *buf, size_t count) { - char ret = 0; - int length = 0; + ssize_t ret = 0; + ssize_t written = 0; - if (address == NULL) { - gf_log_callingfn (THIS->name, GF_LOG_WARNING, "argument invalid"); + for (written = 0; written != count; written += ret) { + ret = sys_write(fd, buf + written, count - written); + if (ret < 0) { + if (errno == EINTR) + ret = 0; + else goto out; } + } - length = strlen (address); - if (length == 0) - goto out; + ret = written; +out: + return ret; +} + +void +gf_free_mig_locks(lock_migration_info_t *locks) +{ + lock_migration_info_t *current = NULL; + lock_migration_info_t *temp = NULL; - if (valid_ipv4_address (address, length) - || valid_ipv6_address (address, length) - || valid_host_name (address, length)) - ret = 1; + if (!locks) + return; -out: - return ret; + if (list_empty(&locks->list)) + return; + + list_for_each_entry_safe(current, temp, &locks->list, list) + { + list_del_init(¤t->list); + GF_FREE(current->client_uid); + GF_FREE(current); + } } -/*Thread safe conversion function*/ -char * -uuid_utoa (uuid_t uuid) +void +_mask_cancellation(void) { - char *uuid_buffer = glusterfs_uuid_buf_get(); - uuid_unparse (uuid, uuid_buffer); - return uuid_buffer; + (void)pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); } -/*Re-entrant conversion function*/ -char * -uuid_utoa_r (uuid_t uuid, char *dst) +void +_unmask_cancellation(void) { - if(!dst) - return NULL; - uuid_unparse (uuid, dst); - return dst; + (void)pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); } -void _get_md5_str (char *out_str, size_t outlen, - const uint8_t *input, int n) +/* This is a wrapper function to add a pointer to a list, + * which doesn't contain list member + */ +struct list_node * +_list_node_add(void *ptr, struct list_head *list, + int (*compare)(struct list_head *, struct list_head *)) { - uint8_t out[MD5_DIGEST_LEN] = {0}; - int j = 0; + struct list_node *node = NULL; - GF_ASSERT (outlen >= (2*MD5_DIGEST_LEN + 1)); - get_md5 (out, input, n); - for (j = 0; j < MD5_DIGEST_LEN; j++) - snprintf(out_str + j * 2, outlen-j*2, "%02x", out[j]); + if (ptr == NULL || list == NULL) + goto out; + node = GF_CALLOC(1, sizeof(struct list_node), gf_common_list_node); + + if (node == NULL) + goto out; + + node->ptr = ptr; + if (compare) + list_add_order(&node->list, list, compare); + else + list_add_tail(&node->list, list); +out: + return node; } -void* gf_array_elem (void *a, int index, size_t elem_size) +struct list_node * +list_node_add(void *ptr, struct list_head *list) { - uint8_t* ptr = a; - return (void*)(ptr + index * elem_size); + return _list_node_add(ptr, list, NULL); } -void -gf_elem_swap (void *x, void *y, size_t l) { - uint8_t *a = x, *b = y, c; - while(l--) { - c = *a; - *a++ = *b; - *b++ = c; - } +struct list_node * +list_node_add_order(void *ptr, struct list_head *list, + int (*compare)(struct list_head *, struct list_head *)) +{ + return _list_node_add(ptr, list, compare); } void -gf_array_insertionsort (void *A, int l, int r, size_t elem_size, - gf_cmp cmp) -{ - int i = l; - int N = r+1; - void *Temp = NULL; - int j = 0; - - for(i = l; i < N; i++) { - Temp = gf_array_elem (A, i, elem_size); - j = i - 1; - while((cmp (Temp, gf_array_elem (A, j, elem_size)) - < 0) && j>=0) { - gf_elem_swap (Temp, gf_array_elem (A, j, elem_size), - elem_size); - Temp = gf_array_elem (A, j, elem_size); - j = j-1; - } - } +list_node_del(struct list_node *node) +{ + if (node == NULL) + return; + + list_del_init(&node->list); + GF_FREE(node); } -int -gf_is_str_int (const char *value) +const char * +fop_enum_to_pri_string(glusterfs_fop_t fop) { - int flag = 0; - char *str = NULL; - char *fptr = NULL; + switch (fop) { + case GF_FOP_OPEN: + case GF_FOP_STAT: + case GF_FOP_FSTAT: + case GF_FOP_LOOKUP: + case GF_FOP_ACCESS: + case GF_FOP_READLINK: + case GF_FOP_OPENDIR: + case GF_FOP_STATFS: + case GF_FOP_READDIR: + case GF_FOP_READDIRP: + case GF_FOP_GETACTIVELK: + case GF_FOP_SETACTIVELK: + case GF_FOP_ICREATE: + case GF_FOP_NAMELINK: + return "HIGH"; + + case GF_FOP_CREATE: + case GF_FOP_FLUSH: + case GF_FOP_LK: + case GF_FOP_INODELK: + case GF_FOP_FINODELK: + case GF_FOP_ENTRYLK: + case GF_FOP_FENTRYLK: + case GF_FOP_UNLINK: + case GF_FOP_SETATTR: + case GF_FOP_FSETATTR: + case GF_FOP_MKNOD: + case GF_FOP_MKDIR: + case GF_FOP_RMDIR: + case GF_FOP_SYMLINK: + case GF_FOP_RENAME: + case GF_FOP_LINK: + case GF_FOP_SETXATTR: + case GF_FOP_GETXATTR: + case GF_FOP_FGETXATTR: + case GF_FOP_FSETXATTR: + case GF_FOP_REMOVEXATTR: + case GF_FOP_FREMOVEXATTR: + case GF_FOP_IPC: + case GF_FOP_LEASE: + return "NORMAL"; + + case GF_FOP_READ: + case GF_FOP_WRITE: + case GF_FOP_FSYNC: + case GF_FOP_TRUNCATE: + case GF_FOP_FTRUNCATE: + case GF_FOP_FSYNCDIR: + case GF_FOP_XATTROP: + case GF_FOP_FXATTROP: + case GF_FOP_RCHECKSUM: + case GF_FOP_ZEROFILL: + case GF_FOP_FALLOCATE: + case GF_FOP_SEEK: + return "LOW"; + + case GF_FOP_NULL: + case GF_FOP_FORGET: + case GF_FOP_RELEASE: + case GF_FOP_RELEASEDIR: + case GF_FOP_GETSPEC: + case GF_FOP_MAXVALUE: + case GF_FOP_DISCARD: + return "LEAST"; + default: + return "UNKNOWN"; + } +} - GF_VALIDATE_OR_GOTO (THIS->name, value, out); +const char * +gf_inode_type_to_str(ia_type_t type) +{ + static const char *const str_ia_type[] = { + "UNKNOWN", "REGULAR FILE", "DIRECTORY", "LINK", + "BLOCK DEVICE", "CHARACTER DEVICE", "PIPE", "SOCKET"}; + return str_ia_type[type]; +} - str = gf_strdup (value); - if (!str) - goto out; +gf_boolean_t +gf_is_zero_filled_stat(struct iatt *buf) +{ + if (!buf) + return 1; + + /* Do not use st_dev because it is transformed to store the xlator id + * in place of the device number. Do not use st_ino because by this time + * we've already mapped the root ino to 1 so it is not guaranteed to be + * 0. + */ + if ((buf->ia_nlink == 0) && (buf->ia_ctime == 0)) + return 1; + + return 0; +} - fptr = str; +void +gf_zero_fill_stat(struct iatt *buf) +{ + buf->ia_nlink = 0; + buf->ia_ctime = 0; +} - while (*str) { - if (!isdigit(*str)) { - flag = 1; - goto out; - } - str++; - } +gf_boolean_t +gf_is_valid_xattr_namespace(char *key) +{ + static char *xattr_namespaces[] = {"trusted.", "system.", "user.", + "security.", NULL}; + int i = 0; -out: - if (fptr) - GF_FREE (fptr); + for (i = 0; xattr_namespaces[i]; i++) { + if (strncmp(key, xattr_namespaces[i], strlen(xattr_namespaces[i])) == 0) + return _gf_true; + } - return flag; + return _gf_false; } -/* - * rounds up nr to power of two. If nr is already a power of two, just returns - * nr - */ -inline int32_t -gf_roundup_power_of_two (uint32_t nr) +ino_t +gfid_to_ino(uuid_t gfid) { - uint32_t result = 1; + ino_t ino = 0; + int32_t i; - if (nr < 0) { - gf_log ("common-utils", GF_LOG_WARNING, - "negative number passed"); - result = -1; - goto out; - } + for (i = 8; i < 16; i++) { + ino <<= 8; + ino += (uint8_t)gfid[i]; + } - while (result < nr) - result *= 2; + return ino; +} -out: - return result; +int +gf_bits_count(uint64_t n) +{ + int val = 0; +#if defined(__GNUC__) || defined(__clang__) + val = __builtin_popcountll(n); +#else + n -= (n >> 1) & 0x5555555555555555ULL; + n = ((n >> 2) & 0x3333333333333333ULL) + (n & 0x3333333333333333ULL); + n = (n + (n >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + n += n >> 8; + n += n >> 16; + n += n >> 32; + val = n & 0xFF; +#endif + return val; } -/* - * rounds up nr to next power of two. If nr is already a power of two, next - * power of two is returned. - */ +int +gf_bits_index(uint64_t n) +{ +#if defined(__GNUC__) || defined(__clang__) + return __builtin_ffsll(n) - 1; +#else + return ffsll(n) - 1; +#endif +} -/* - * rounds up nr to next power of two. If nr is already a power of two, next - * power of two is returned. - */ +const char * +gf_fop_string(glusterfs_fop_t fop) +{ + if ((fop > GF_FOP_NULL) && (fop < GF_FOP_MAXVALUE)) + return gf_fop_list[fop]; + return "INVALID"; +} -inline int32_t -gf_roundup_next_power_of_two (uint32_t nr) +int +gf_fop_int(char *fop) { - int32_t result = 1; + int i = 0; - if (nr < 0) { - gf_log ("common-utils", GF_LOG_WARNING, - "negative number passed"); - result = -1; - goto out; - } + for (i = GF_FOP_NULL + 1; i < GF_FOP_MAXVALUE; i++) { + if (strcasecmp(fop, gf_fop_list[i]) == 0) + return i; + } + return -1; +} - while (result <= nr) - result *= 2; +int +close_fds_except(int *fdv, size_t count) +{ + int i = 0; + size_t j = 0; + gf_boolean_t should_close = _gf_true; +#ifdef GF_LINUX_HOST_OS + DIR *d = NULL; + struct dirent *de = NULL; + struct dirent scratch[2] = { + { + 0, + }, + }; + char *e = NULL; -out: - return result; + d = sys_opendir("/proc/self/fd"); + if (!d) + return -1; + + for (;;) { + should_close = _gf_true; + + errno = 0; + de = sys_readdir(d, scratch); + if (!de || errno != 0) + break; + i = strtoul(de->d_name, &e, 10); + if (*e != '\0' || i == dirfd(d)) + continue; + + for (j = 0; j < count; j++) { + if (i == fdv[j]) { + should_close = _gf_false; + break; + } + } + if (should_close) + sys_close(i); + } + sys_closedir(d); +#else /* !GF_LINUX_HOST_OS */ + struct rlimit rl; + int ret = -1; + + ret = getrlimit(RLIMIT_NOFILE, &rl); + if (ret) + return ret; + + for (i = 0; i < rl.rlim_cur; i++) { + should_close = _gf_true; + for (j = 0; j < count; j++) { + if (i == fdv[j]) { + should_close = _gf_false; + break; + } + } + if (should_close) + sys_close(i); + } +#endif /* !GF_LINUX_HOST_OS */ + return 0; } +/** + * gf_getgrouplist - get list of groups to which a user belongs + * + * A convenience wrapper for getgrouplist(3). + * + * @param user - same as in getgrouplist(3) + * @param group - same as in getgrouplist(3) + * @param groups - pointer to a gid_t pointer + * + * gf_getgrouplist allocates a gid_t buffer which is big enough to + * hold the list of auxiliary group ids for user, up to the GF_MAX_AUX_GROUPS + * threshold. Upon successful invocation groups will be pointed to that buffer. + * + * @return success: the number of auxiliary group ids retrieved + * failure: -1 + */ int -validate_brick_name (char *brick) +gf_getgrouplist(const char *user, gid_t group, gid_t **groups) { - char *delimiter = NULL; - int ret = 0; - delimiter = strrchr (brick, ':'); - if (!delimiter || delimiter == brick - || *(delimiter+1) != '/') - ret = -1; + int ret = -1; + int ngroups = SMALL_GROUP_COUNT; - return ret; + *groups = GF_CALLOC(sizeof(gid_t), ngroups, gf_common_mt_groups_t); + if (!*groups) + return -1; + + /* + * We are running getgrouplist() in a loop until we succeed (or hit + * certain exit conditions, see the comments below). This is because + * the indicated number of auxiliary groups that we obtain in case of + * the failure of the first invocation is not guaranteed to keep its + * validity upon the next invocation with a gid buffer of that size. + */ + for (;;) { + int ngroups_old = ngroups; + ret = getgrouplist(user, group, *groups, &ngroups); + if (ret != -1) + break; + + if (ngroups >= GF_MAX_AUX_GROUPS) { + /* + * This should not happen as GF_MAX_AUX_GROUPS is set + * to the max value of number of supported auxiliary + * groups across all platforms supported by GlusterFS. + * However, if it still happened some way, we wouldn't + * care about the incompleteness of the result, we'd + * just go on with what we got. + */ + return GF_MAX_AUX_GROUPS; + } else if (ngroups <= ngroups_old) { + /* + * There is an edge case that getgrouplist() fails but + * ngroups remains the same. This is actually not + * specified in getgrouplist(3), but implementations + * can do this upon internal failure[1]. To avoid + * falling into an infinite loop when this happens, we + * break the loop if the getgrouplist call failed + * without an increase in the indicated group number. + * + * [1] + * https://sourceware.org/git/?p=glibc.git;a=blob;f=grp/initgroups.c;hb=refs/heads/release/2.25/master#l168 + */ + GF_FREE(*groups); + return -1; + } + + *groups = GF_REALLOC(*groups, ngroups * sizeof(gid_t)); + if (!*groups) + return -1; + } + return ret; } -char * -get_host_name (char *word, char **host) +int +glusterfs_compute_sha256(const unsigned char *content, size_t size, + char *sha256_hash) { - char *delimiter = NULL; - delimiter = strrchr (word, ':'); - if (delimiter) - *delimiter = '\0'; - else - return NULL; - *host = word; - return *host; + SHA256_CTX sha256; + + SHA256_Init(&sha256); + SHA256_Update(&sha256, (const unsigned char *)(content), size); + SHA256_Final((unsigned char *)sha256_hash, &sha256); + + return 0; } +/* * Safe wrapper function for strncpy. + * This wrapper makes sure that when there is no null byte among the first n in + * source srting for strncpy function call, the string placed in dest will be + * null-terminated. + */ char * -get_path_name (char *word, char **path) +gf_strncpy(char *dest, const char *src, const size_t dest_size) { - char *delimiter = NULL; - delimiter = strchr (word, '/'); - if (!delimiter) - return NULL; - *path = delimiter; - return *path; + strncpy(dest, src, dest_size - 1); + dest[dest_size - 1] = '\0'; + return dest; } -void -gf_path_strip_trailing_slashes (char *path) +int +gf_replace_old_iatt_in_dict(dict_t *xdata) { - int i = 0; - int len = 0; + int ret; + struct old_iatt *o_iatt; /* old iatt structure */ + struct iatt *c_iatt; /* current iatt */ - if (!path) - return; + if (!xdata) { + return 0; + } - len = strlen (path); - for (i = len - 1; i > 0; i--) { - if (path[i] != '/') - break; - } + ret = dict_get_bin(xdata, DHT_IATT_IN_XDATA_KEY, (void **)&c_iatt); + if (ret < 0) { + return 0; + } - if (i < (len -1)) - path [i+1] = '\0'; + o_iatt = GF_CALLOC(1, sizeof(struct old_iatt), gf_common_mt_char); + if (!o_iatt) { + return -1; + } - return; + oldiatt_from_iatt(o_iatt, c_iatt); + + ret = dict_set_bin(xdata, DHT_IATT_IN_XDATA_KEY, o_iatt, + sizeof(struct old_iatt)); + if (ret) { + GF_FREE(o_iatt); + } + + return ret; } -uint64_t -get_mem_size () +int +gf_replace_new_iatt_in_dict(dict_t *xdata) { - uint64_t memsize = -1; + int ret; + struct old_iatt *o_iatt; /* old iatt structure */ + struct iatt *c_iatt; /* new iatt */ -#ifdef __linux__ - FILE *fp = NULL; - char line[1028] = {0,}; + if (!xdata) { + return 0; + } - fp = fopen ("/proc/meminfo", "r"); - if (!fp) { - gf_log ("common-utils", GF_LOG_DEBUG, - "Could not open /proc/meminfo"); - return memsize; - } + ret = dict_get_bin(xdata, DHT_IATT_IN_XDATA_KEY, (void **)&o_iatt); + if (ret < 0) { + return 0; + } - while (fgets (line, sizeof (line), fp) != 0) { - if (strncmp (line, "MemTotal:", 9) == 0) { - sscanf (line, "%*s %"SCNu64" kB", &memsize); - memsize *= 1024; //convert to bytes - gf_log ("common-utils", GF_LOG_INFO, - "Total Mem: %"PRIu64, memsize); - break; - } - } -#endif - // TODO: Methods for other platforms + c_iatt = GF_CALLOC(1, sizeof(struct iatt), gf_common_mt_char); + if (!c_iatt) { + return -1; + } + + iatt_from_oldiatt(c_iatt, o_iatt); + + ret = dict_set_bin(xdata, DHT_IATT_IN_XDATA_KEY, c_iatt, + sizeof(struct iatt)); + if (ret) { + GF_FREE(c_iatt); + } - return memsize; + return ret; } +xlator_cmdline_option_t * +find_xlator_option_in_cmd_args_t(const char *option_name, cmd_args_t *args) +{ + xlator_cmdline_option_t *pos = NULL; + xlator_cmdline_option_t *tmp = NULL; + + list_for_each_entry_safe(pos, tmp, &args->xlator_options, cmd_args) + { + if (strcmp(pos->key, option_name) == 0) + return pos; + } + return NULL; +} int -gf_client_pid_check (gf_client_pid_t npid) +gf_d_type_from_ia_type(ia_type_t type) +{ + switch (type) { + case IA_IFDIR: + return DT_DIR; + case IA_IFCHR: + return DT_CHR; + case IA_IFBLK: + return DT_BLK; + case IA_IFIFO: + return DT_FIFO; + case IA_IFLNK: + return DT_LNK; + case IA_IFREG: + return DT_REG; + case IA_IFSOCK: + return DT_SOCK; + default: + return DT_UNKNOWN; + } +} + +int +gf_nanosleep(uint64_t nsec) +{ + struct timespec req; + struct timespec rem; + int ret = -1; + + req.tv_sec = nsec / GF_SEC_IN_NS; + req.tv_nsec = nsec % GF_SEC_IN_NS; + + do { + ret = nanosleep(&req, &rem); + req = rem; + } while (ret == -1 && errno == EINTR); + + return ret; +} + +int +gf_syncfs(int fd) +{ + int ret = 0; +#if defined(HAVE_SYNCFS) + /* Linux with glibc recent enough. */ + ret = syncfs(fd); +#elif defined(HAVE_SYNCFS_SYS) + /* Linux with no library function. */ + ret = syscall(SYS_syncfs, fd); +#else + /* Fallback to generic UNIX stuff. */ + sync(); +#endif + return ret; +} + +char ** +get_xattrs_to_heal() { - return ( (npid > GF_CLIENT_PID_MIN) && (npid < GF_CLIENT_PID_MAX) ) - ? 0 : -1; + return xattrs_to_heal; } |
