diff options
Diffstat (limited to 'xlators/protocol/server/src/server-helpers.c')
-rw-r--r-- | xlators/protocol/server/src/server-helpers.c | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/xlators/protocol/server/src/server-helpers.c b/xlators/protocol/server/src/server-helpers.c new file mode 100644 index 000000000..b51c11aa9 --- /dev/null +++ b/xlators/protocol/server/src/server-helpers.c @@ -0,0 +1,586 @@ +/* + Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.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/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "server-protocol.h" +#include "server-helpers.h" + + +/* server_loc_fill - derive a loc_t for a given inode number + * + * NOTE: make sure that @loc is empty, because any pointers it holds with reference will + * be leaked after returning from here. + */ +int +server_loc_fill (loc_t *loc, server_state_t *state, + ino_t ino, ino_t par, + const char *name, const char *path) +{ + inode_t *inode = NULL; + inode_t *parent = NULL; + int32_t ret = -1; + char *dentry_path = NULL; + + + GF_VALIDATE_OR_GOTO ("server", loc, out); + GF_VALIDATE_OR_GOTO ("server", state, out); + GF_VALIDATE_OR_GOTO ("server", path, out); + + /* anything beyond this point is success */ + ret = 0; + loc->ino = ino; + inode = loc->inode; + if (inode == NULL) { + if (ino) + inode = inode_search (state->itable, ino, NULL); + + if ((inode == NULL) && + (par && name)) + inode = inode_search (state->itable, par, name); + + loc->inode = inode; + if (inode) + loc->ino = inode->ino; + } + + parent = loc->parent; + if (parent == NULL) { + if (inode) + parent = inode_parent (inode, par, name); + else + parent = inode_search (state->itable, par, NULL); + loc->parent = parent; + } + + if (name && parent) { + ret = inode_path (parent, name, &dentry_path); + if (ret < 0) { + gf_log (state->bound_xl->name, GF_LOG_DEBUG, + "failed to build path for %"PRId64"/%s: %s", + parent->ino, name, strerror (-ret)); + } + } else if (inode) { + ret = inode_path (inode, NULL, &dentry_path); + if (ret < 0) { + gf_log (state->bound_xl->name, GF_LOG_DEBUG, + "failed to build path for %"PRId64": %s", + inode->ino, strerror (-ret)); + + inode_unref (loc->inode); + loc->inode = NULL; + } + } + + if (dentry_path) { + if (strcmp (dentry_path, path)) { + gf_log (state->bound_xl->name, GF_LOG_DEBUG, + "paths differ for inode(%"PRId64"): " + "client path = %s. dentry path = %s", + ino, path, dentry_path); + } + + loc->path = dentry_path; + loc->name = strrchr (loc->path, '/'); + if (loc->name) + loc->name++; + } else { + loc->path = strdup (path); + loc->name = strrchr (loc->path, '/'); + if (loc->name) + loc->name++; + } + +out: + return ret; +} + +/* + * stat_to_str - convert struct stat to a ASCII string + * @stbuf: struct stat pointer + * + * not for external reference + */ +char * +stat_to_str (struct stat *stbuf) +{ + char *tmp_buf = NULL; + + uint64_t dev = stbuf->st_dev; + uint64_t ino = stbuf->st_ino; + uint32_t mode = stbuf->st_mode; + uint32_t nlink = stbuf->st_nlink; + uint32_t uid = stbuf->st_uid; + uint32_t gid = stbuf->st_gid; + uint64_t rdev = stbuf->st_rdev; + uint64_t size = stbuf->st_size; + uint32_t blksize = stbuf->st_blksize; + uint64_t blocks = stbuf->st_blocks; + uint32_t atime = stbuf->st_atime; + uint32_t mtime = stbuf->st_mtime; + uint32_t ctime = stbuf->st_ctime; + + uint32_t atime_nsec = ST_ATIM_NSEC(stbuf); + uint32_t mtime_nsec = ST_MTIM_NSEC(stbuf); + uint32_t ctime_nsec = ST_CTIM_NSEC(stbuf); + + + asprintf (&tmp_buf, + GF_STAT_PRINT_FMT_STR, + dev, + ino, + mode, + nlink, + uid, + gid, + rdev, + size, + blksize, + blocks, + atime, + atime_nsec, + mtime, + mtime_nsec, + ctime, + ctime_nsec); + + return tmp_buf; +} + + +void +server_loc_wipe (loc_t *loc) +{ + if (loc->parent) + inode_unref (loc->parent); + if (loc->inode) + inode_unref (loc->inode); + if (loc->path) + free ((char *)loc->path); +} + +void +free_state (server_state_t *state) +{ + transport_t *trans = NULL; + + trans = state->trans; + + if (state->fd) + fd_unref (state->fd); + + transport_unref (trans); + + if (state->xattr_req) + dict_unref (state->xattr_req); + + FREE (state); +} + + +call_frame_t * +server_copy_frame (call_frame_t *frame) +{ + call_frame_t *new_frame = NULL; + server_state_t *state = NULL, *new_state = NULL; + + state = frame->root->state; + + new_frame = copy_frame (frame); + + new_state = CALLOC (1, sizeof (server_state_t)); + + new_frame->root->op = frame->root->op; + new_frame->root->type = frame->root->type; + new_frame->root->trans = state->trans; + new_frame->root->state = new_state; + + new_state->bound_xl = state->bound_xl; + new_state->trans = transport_ref (state->trans); + new_state->itable = state->itable; + + return new_frame; +} + +int32_t +gf_add_locker (struct _lock_table *table, + loc_t *loc, + fd_t *fd, + pid_t pid) +{ + int32_t ret = -1; + struct _locker *new = NULL; + uint8_t dir = 0; + + new = CALLOC (1, sizeof (struct _locker)); + if (new == NULL) { + gf_log ("server", GF_LOG_ERROR, + "failed to allocate memory for \'struct _locker\'"); + goto out; + } + INIT_LIST_HEAD (&new->lockers); + + if (fd == NULL) { + loc_copy (&new->loc, loc); + dir = S_ISDIR (new->loc.inode->st_mode); + } else { + new->fd = fd_ref (fd); + dir = S_ISDIR (fd->inode->st_mode); + } + + new->pid = pid; + + LOCK (&table->lock); + { + if (dir) + list_add_tail (&new->lockers, &table->dir_lockers); + else + list_add_tail (&new->lockers, &table->file_lockers); + } + UNLOCK (&table->lock); +out: + return ret; +} + +int32_t +gf_del_locker (struct _lock_table *table, + loc_t *loc, + fd_t *fd, + pid_t pid) +{ + struct _locker *locker = NULL, *tmp = NULL; + int32_t ret = 0; + uint8_t dir = 0; + struct list_head *head = NULL; + struct list_head del; + + INIT_LIST_HEAD (&del); + + if (fd) { + dir = S_ISDIR (fd->inode->st_mode); + } else { + dir = S_ISDIR (loc->inode->st_mode); + } + + LOCK (&table->lock); + { + if (dir) { + head = &table->dir_lockers; + } else { + head = &table->file_lockers; + } + + list_for_each_entry_safe (locker, tmp, head, lockers) { + if (locker->fd && + fd && + (locker->fd == fd) && (locker->pid == pid)) { + list_move_tail (&locker->lockers, &del); + } else if (locker->loc.inode && + loc && + (locker->loc.inode == loc->inode) && + (locker->pid == pid)) { + list_move_tail (&locker->lockers, &del); + } + } + } + UNLOCK (&table->lock); + + tmp = NULL; + locker = NULL; + + list_for_each_entry_safe (locker, tmp, &del, lockers) { + list_del_init (&locker->lockers); + if (locker->fd) + fd_unref (locker->fd); + else + loc_wipe (&locker->loc); + + free (locker); + } + + return ret; +} + +int32_t +gf_direntry_to_bin (dir_entry_t *head, + char **bufferp) +{ + dir_entry_t *trav = NULL; + uint32_t len = 0; + uint32_t this_len = 0; + char *buffer = NULL; + size_t buflen = -1; + char *ptr = NULL; + char *tmp_buf = NULL; + + trav = head->next; + while (trav) { + len += strlen (trav->name); + len += 1; + len += strlen (trav->link); + len += 1; /* for '\n' */ + len += 256; // max possible for statbuf; + trav = trav->next; + } + + buffer = CALLOC (1, len); + if (buffer == NULL) { + gf_log ("server", GF_LOG_ERROR, + "failed to allocate memory for buffer"); + goto out; + } + + ptr = buffer; + trav = head->next; + while (trav) { + tmp_buf = stat_to_str (&trav->buf); + /* tmp_buf will have \n before \0 */ + + this_len = sprintf (ptr, "%s/%s%s\n", + trav->name, tmp_buf, + trav->link); + + FREE (tmp_buf); + trav = trav->next; + ptr += this_len; + } + if (bufferp) + *bufferp = buffer; + buflen = strlen (buffer); + +out: + return buflen; +} + + +static struct _lock_table * +gf_lock_table_new (void) +{ + struct _lock_table *new = NULL; + + new = CALLOC (1, sizeof (struct _lock_table)); + if (new == NULL) { + gf_log ("server-protocol", GF_LOG_CRITICAL, + "failed to allocate memory for new lock table"); + goto out; + } + INIT_LIST_HEAD (&new->dir_lockers); + INIT_LIST_HEAD (&new->file_lockers); + LOCK_INIT (&new->lock); +out: + return new; +} + + +int +server_connection_destroy (xlator_t *this, server_connection_t *conn) +{ + + call_frame_t *frame = NULL, *tmp_frame = NULL; + xlator_t *bound_xl = NULL; + int32_t ret = -1; + server_state_t *state = NULL; + struct list_head file_lockers; + struct list_head dir_lockers; + struct _lock_table *ltable = NULL; + struct _locker *locker = NULL, *tmp = NULL; + struct flock flock = {0,}; + + + bound_xl = (xlator_t *) (conn->bound_xl); + + if (bound_xl) { + /* trans will have ref_count = 1 after this call, but its + ok since this function is called in + GF_EVENT_TRANSPORT_CLEANUP */ + frame = create_frame (this, this->ctx->pool); + + pthread_mutex_lock (&(conn->lock)); + { + if (conn->ltable) { + ltable = conn->ltable; + conn->ltable = NULL; + } + } + pthread_mutex_unlock (&conn->lock); + + INIT_LIST_HEAD (&file_lockers); + INIT_LIST_HEAD (&dir_lockers); + + LOCK (<able->lock); + { + list_splice_init (<able->file_lockers, + &file_lockers); + + list_splice_init (<able->dir_lockers, &dir_lockers); + } + UNLOCK (<able->lock); + free (ltable); + + flock.l_type = F_UNLCK; + flock.l_start = 0; + flock.l_len = 0; + list_for_each_entry_safe (locker, + tmp, &file_lockers, lockers) { + tmp_frame = copy_frame (frame); + /* + pid = 0 is a special case that tells posix-locks + to release all locks from this transport + */ + tmp_frame->root->pid = 0; + tmp_frame->root->trans = conn; + + if (locker->fd) { + STACK_WIND (tmp_frame, server_nop_cbk, + bound_xl, + bound_xl->fops->finodelk, + locker->fd, F_SETLK, &flock); + fd_unref (locker->fd); + } else { + STACK_WIND (tmp_frame, server_nop_cbk, + bound_xl, + bound_xl->fops->inodelk, + &(locker->loc), F_SETLK, &flock); + loc_wipe (&locker->loc); + } + + list_del_init (&locker->lockers); + free (locker); + } + + tmp = NULL; + locker = NULL; + list_for_each_entry_safe (locker, tmp, &dir_lockers, lockers) { + tmp_frame = copy_frame (frame); + + tmp_frame->root->pid = 0; + tmp_frame->root->trans = conn; + + if (locker->fd) { + STACK_WIND (tmp_frame, server_nop_cbk, + bound_xl, + bound_xl->fops->fentrylk, + locker->fd, NULL, + ENTRYLK_UNLOCK, ENTRYLK_WRLCK); + fd_unref (locker->fd); + } else { + STACK_WIND (tmp_frame, server_nop_cbk, + bound_xl, + bound_xl->fops->entrylk, + &(locker->loc), NULL, + ENTRYLK_UNLOCK, ENTRYLK_WRLCK); + loc_wipe (&locker->loc); + } + + list_del_init (&locker->lockers); + free (locker); + } + + state = CALL_STATE (frame); + if (state) + free (state); + STACK_DESTROY (frame->root); + + pthread_mutex_lock (&(conn->lock)); + { + if (conn->fdtable) { + gf_fd_fdtable_destroy (conn->fdtable); + conn->fdtable = NULL; + } + } + pthread_mutex_unlock (&conn->lock); + + } + + gf_log (this->name, GF_LOG_INFO, "destroyed connection of %s", + conn->id); + + FREE (conn->id); + FREE (conn); + + return ret; +} + + +server_connection_t * +server_connection_get (xlator_t *this, const char *id) +{ + server_connection_t *conn = NULL; + server_connection_t *trav = NULL; + server_conf_t *conf = NULL; + + conf = this->private; + + pthread_mutex_lock (&conf->mutex); + { + list_for_each_entry (trav, &conf->conns, list) { + if (!strcmp (id, trav->id)) { + conn = trav; + break; + } + } + + if (!conn) { + conn = (void *) CALLOC (1, sizeof (*conn)); + + conn->id = strdup (id); + conn->fdtable = gf_fd_fdtable_alloc (); + conn->ltable = gf_lock_table_new (); + + pthread_mutex_init (&conn->lock, NULL); + + list_add (&conn->list, &conf->conns); + } + + conn->ref++; + } + pthread_mutex_unlock (&conf->mutex); + + return conn; +} + + +void +server_connection_put (xlator_t *this, server_connection_t *conn) +{ + server_conf_t *conf = NULL; + server_connection_t *todel = NULL; + + conf = this->private; + + pthread_mutex_lock (&conf->mutex); + { + conn->ref--; + + if (!conn->ref) { + list_del_init (&conn->list); + todel = conn; + } + } + pthread_mutex_unlock (&conf->mutex); + + if (todel) { + server_connection_destroy (this, todel); + } + + return; +} |