diff options
Diffstat (limited to 'xlators/features/trash/src')
| -rw-r--r-- | xlators/features/trash/src/Makefile.am | 12 | ||||
| -rw-r--r-- | xlators/features/trash/src/trash-mem-types.h | 33 | ||||
| -rw-r--r-- | xlators/features/trash/src/trash.c | 3759 | ||||
| -rw-r--r-- | xlators/features/trash/src/trash.h | 128 |
4 files changed, 2512 insertions, 1420 deletions
diff --git a/xlators/features/trash/src/Makefile.am b/xlators/features/trash/src/Makefile.am index 4671d06d309..8557e7171af 100644 --- a/xlators/features/trash/src/Makefile.am +++ b/xlators/features/trash/src/Makefile.am @@ -1,15 +1,19 @@ +if WITH_SERVER xlator_LTLIBRARIES = trash.la -xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/testing/features +endif +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features -trash_la_LDFLAGS = -module -avoidversion +trash_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) trash_la_SOURCES = trash.c trash_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la noinst_HEADERS = trash.h trash-mem-types.h -AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\ - -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS) +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/rpc/xdr/src -I$(top_builddir)/rpc/xdr/src + +AM_CFLAGS = -Wall $(GF_CFLAGS) CLEANFILES = diff --git a/xlators/features/trash/src/trash-mem-types.h b/xlators/features/trash/src/trash-mem-types.h index 6608abf6a17..43353c8f095 100644 --- a/xlators/features/trash/src/trash-mem-types.h +++ b/xlators/features/trash/src/trash-mem-types.h @@ -1,33 +1,22 @@ /* - Copyright (c) 2008-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 __TRASH_MEM_TYPES_H__ #define __TRASH_MEM_TYPES_H__ -#include "mem-types.h" +#include <glusterfs/mem-types.h> enum gf_trash_mem_types_ { - gf_trash_mt_trash_local_t = gf_common_mt_end + 1, - gf_trash_mt_trash_private_t, - gf_trash_mt_char, - gf_trash_mt_trash_elim_pattern_t, - gf_trash_mt_end + gf_trash_mt_trash_private_t = gf_common_mt_end + 1, + gf_trash_mt_char, + gf_trash_mt_uuid, + gf_trash_mt_trash_elim_path, + gf_trash_mt_end }; #endif - diff --git a/xlators/features/trash/src/trash.c b/xlators/features/trash/src/trash.c index 42ee79a9691..7d09cba3e9c 100644 --- a/xlators/features/trash/src/trash.c +++ b/xlators/features/trash/src/trash.c @@ -1,1562 +1,2653 @@ /* - Copyright (c) 2006-2011 Gluster, Inc. <http://www.gluster.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 + Copyright (c) 2006-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ #include "trash.h" #include "trash-mem-types.h" +#include <glusterfs/syscall.h> -int32_t -trash_ftruncate_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iovec *vector, int32_t count, - struct iatt *stbuf, struct iobref *iobuf); +#define root_gfid \ + (uuid_t) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } +#define trash_gfid \ + (uuid_t) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 } +#define internal_op_gfid \ + (uuid_t) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6 } int32_t -trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *prebuf, struct iatt *postbuf); +trash_truncate_writev_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata); int32_t -trash_truncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *stbuf, struct iatt *preparent, - struct iatt *postparent); +trash_truncate_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *stbuf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata); int32_t -trash_unlink_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf, - struct iatt *preoldparent, struct iatt *postoldparent, - struct iatt *prenewparent, struct iatt *postnewparent); +trash_unlink_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata); +/* Common routines used in this translator */ -void -trash_local_wipe (trash_local_t *local) +/** + * When a directory/file is created under trash directory, it should have + * the same permission as before. This function will fetch permission from + * the existing directory and returns the same + */ +mode_t +get_permission(char *path) { - if (!local) - goto out; - - loc_wipe (&local->loc); - loc_wipe (&local->newloc); - - if (local->fd) - fd_unref (local->fd); - - if (local->newfd) - fd_unref (local->newfd); + mode_t mode = 0755; + struct stat sbuf = { + 0, + }; + struct iatt ibuf = { + 0, + }; + int ret = 0; + + ret = sys_stat(path, &sbuf); + if (!ret) { + iatt_from_stat(&ibuf, &sbuf); + mode = st_mode_from_ia(ibuf.ia_prot, ibuf.ia_type); + } else + gf_log("trash", GF_LOG_DEBUG, + "stat on %s failed" + " using default", + path); + return mode; +} - GF_FREE (local); +/** + * For normalization, trash directory name is stored inside priv structure as + * '/trash_directory/'. As a result the trailing and leading slashes are being + * striped out for additional usage. + */ +int +extract_trash_directory(char *priv_value, const char **trash_directory) +{ + char *tmp = NULL; + int ret = 0; + + GF_VALIDATE_OR_GOTO("trash", priv_value, out); + + tmp = gf_strdup(priv_value + 1); + if (!tmp) { + ret = ENOMEM; + goto out; + } + if (tmp[strlen(tmp) - 1] == '/') + tmp[strlen(tmp) - 1] = '\0'; + *trash_directory = gf_strdup(tmp); + if (!(*trash_directory)) { + ret = ENOMEM; + goto out; + } out: - return; + if (tmp) + GF_FREE(tmp); + return ret; } -int32_t -trash_common_unwind_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *preparent, struct iatt *postparent) +/** + * The trash directory path should be append at beginning of file path for + * delete or truncate operations. Normal trashing moves the contents to + * trash directory and trashing done by internal operations are moved to + * internal_op directory inside trash. + */ +void +copy_trash_path(const char *priv_value, gf_boolean_t internal, char *path, + size_t path_size) { - TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, preparent, postparent); - return 0; + char trash_path[PATH_MAX] = { + 0, + }; + + strncpy(trash_path, priv_value, sizeof(trash_path)); + trash_path[sizeof(trash_path) - 1] = 0; + if (internal) + strncat(trash_path, "internal_op/", + sizeof(trash_path) - strlen(trash_path) - 1); + + strncpy(path, trash_path, path_size); + path[path_size - 1] = 0; } -int32_t -trash_unlink_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *stbuf, struct iatt *preparent, - struct iatt *postparent) +/** + * This function performs the reverse operation of copy_trash_path(). It gives + * out a pointer, whose starting value will be the path inside trash directory, + * similar to original path. + */ +void +remove_trash_path(const char *path, gf_boolean_t internal, char **rem_path) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *tmp_path = NULL; - char *tmp_dirname = NULL; - char *dir_name = NULL; - int32_t count = 0; - int32_t loop_count = 0; - int i = 0; - loc_t tmp_loc = {0,}; - - local = frame->local; - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_ERROR, "out of memory"); - goto out; - } - loop_count = local->loop_count; - - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - i++; - if (i > loop_count) - break; - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_ERROR, "out of memory"); - goto out; - } - - tmp_loc.path = tmp_path; - - /* TODO:create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, trash_unlink_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - - goto out; - } - - if (op_ret == 0) { - dir_name = dirname (tmp_str); - if (strcmp((char*)cookie, dir_name) == 0) { - tmp_loc.path = local->newpath; - STACK_WIND (frame, trash_unlink_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc, &tmp_loc); - goto out; - } - } - - LOCK (&frame->lock); - { - loop_count = ++local->loop_count; - } - UNLOCK (&frame->lock); - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - i++; - if ((i > loop_count) || (count > PATH_MAX)) - break; - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_ERROR, "out of memory"); - goto out; - } - tmp_loc.path = tmp_path; - - STACK_WIND_COOKIE (frame, trash_unlink_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - -out: - GF_FREE (cookie); - if (tmp_str) - GF_FREE (tmp_str); + if (rem_path == NULL) { + return; + } - return 0; + *rem_path = strchr(path + 1, '/'); + if (internal) + *rem_path = strchr(*rem_path + 1, '/'); } -int32_t -trash_rename_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *stbuf, struct iatt *preparent, - struct iatt *postparent); - -int32_t -trash_unlink_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf, - struct iatt *preoldparent, struct iatt *postoldparent, - struct iatt *prenewparent, struct iatt *postnewparent) +/** + * Checks whether the given path reside under the specified eliminate path + */ +int +check_whether_eliminate_path(trash_elim_path *trav, const char *path) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *dir_name = NULL; - char *tmp_cookie = NULL; - loc_t tmp_loc = {0,}; - - local = frame->local; - - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - dir_name = dirname (tmp_str); - - tmp_loc.path = dir_name; - - tmp_cookie = gf_strdup (dir_name); - if (!tmp_cookie) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, trash_unlink_mkdir_cbk, tmp_cookie, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->mkdir, - &tmp_loc, 0755, NULL); - - GF_FREE (tmp_str); - - return 0; - } - - if ((op_ret == -1) && (op_errno == ENOTDIR)) { - - gf_log (this->name, GF_LOG_DEBUG, - "target(%s) exists, cannot keep the copy, deleting", - local->newpath); + int match = 0; - STACK_WIND (frame, trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, &local->loc); - - return 0; + while (trav) { + if (strncmp(path, trav->path, strlen(trav->path)) == 0) { + match++; + break; } - - if ((op_ret == -1) && (op_errno == EISDIR)) { - gf_log (this->name, GF_LOG_DEBUG, - "target(%s) exists as directory, cannot keep copy, " - "deleting", local->newpath); - - STACK_WIND (frame, trash_common_unwind_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->unlink, &local->loc); - return 0; - } - - /* All other cases, unlink should return success */ - TRASH_STACK_UNWIND (unlink, frame, 0, op_errno, &local->preparent, - &local->postparent); - - return 0; + trav = trav->next; + } + return match; } - - -int32_t -trash_common_unwind_buf_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *prebuf, struct iatt *postbuf) +/** + * Stores the eliminate path into internal eliminate path structure + */ +int +store_eliminate_path(char *str, trash_elim_path **eliminate) { - TRASH_STACK_UNWIND (truncate, frame, op_ret, op_errno, prebuf, postbuf); - return 0; + trash_elim_path *trav = NULL; + char *component = NULL; + char elm_path[PATH_MAX] = { + 0, + }; + int ret = 0; + char *strtokptr = NULL; + + if ((str == NULL) || (eliminate == NULL)) { + ret = EINVAL; + goto out; + } + + component = strtok_r(str, ",", &strtokptr); + while (component) { + trav = GF_CALLOC(1, sizeof(*trav), gf_trash_mt_trash_elim_path); + if (!trav) { + ret = ENOMEM; + goto out; + } + if (component[0] == '/') + sprintf(elm_path, "%s", component); + else + sprintf(elm_path, "/%s", component); + + if (component[strlen(component) - 1] != '/') + strncat(elm_path, "/", sizeof(elm_path) - strlen(elm_path) - 1); + + trav->path = gf_strdup(elm_path); + if (!trav->path) { + ret = ENOMEM; + gf_log("trash", GF_LOG_DEBUG, "out of memory"); + GF_FREE(trav); + goto out; + } + trav->next = *eliminate; + *eliminate = trav; + component = strtok_r(NULL, ",", &strtokptr); + } +out: + return ret; } -int -trash_common_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *stbuf, - struct iatt *preoldparent, struct iatt *postoldparent, - struct iatt *prenewparent, struct iatt *postnewparent) +/** + * Appends time stamp to given string + */ +void +append_time_stamp(char *name, size_t name_size) { - TRASH_STACK_UNWIND (rename, frame, op_ret, op_errno, stbuf, preoldparent, - postoldparent, prenewparent, postnewparent); - return 0; + int i; + char timestr[GF_TIMESTR_SIZE] = { + 0, + }; + + gf_time_fmt(timestr, sizeof(timestr), gf_time(), gf_timefmt_F_HMS); + + /* removing white spaces in timestamp */ + for (i = 0; i < strlen(timestr); i++) { + if (timestr[i] == ' ') + timestr[i] = '_'; + } + strncat(name, "_", name_size - strlen(name) - 1); + strncat(name, timestr, name_size - strlen(name) - 1); } +/* * + * Check whether delete/rename operation is permitted on + * trash directory + */ -int32_t -trash_unlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf) +gf_boolean_t +check_whether_op_permitted(trash_private_t *priv, loc_t *loc) { - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - loc_t new_loc = {0,}; + if ((priv->state && (gf_uuid_compare(loc->inode->gfid, trash_gfid) == 0))) + return _gf_false; + if (priv->internal && + (gf_uuid_compare(loc->inode->gfid, internal_op_gfid) == 0)) + return _gf_false; - priv = this->private; - local = frame->local; - - if (-1 == op_ret) { - gf_log (this->name, GF_LOG_DEBUG, "%s: %s", - local->loc.path, strerror (op_errno)); - goto fail; - } - - if ((buf->ia_size == 0) || - (buf->ia_size > priv->max_trash_file_size)) { - /* if the file is too big or zero, just unlink it */ - - if (buf->ia_size > priv->max_trash_file_size) { - gf_log (this->name, GF_LOG_DEBUG, - "%s: file size too big (%"PRId64") to " - "move into trash directory", - local->loc.path, buf->ia_size); - } - - STACK_WIND (frame, trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, &local->loc); - return 0; - } - - new_loc.path = local->newpath; - - STACK_WIND (frame, trash_unlink_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc, &new_loc); + return _gf_true; +} - return 0; +/** + * Wipe the memory used by trash location variable + */ +void +trash_local_wipe(trash_local_t *local) +{ + if (!local) + goto out; -fail: - TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, buf, - NULL); + loc_wipe(&local->loc); + loc_wipe(&local->newloc); - return 0; + if (local->fd) + fd_unref(local->fd); + if (local->newfd) + fd_unref(local->newfd); + mem_put(local); +out: + return; } -int32_t -trash_rename_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf, - struct iatt *preoldparent, struct iatt *postoldparent, - struct iatt *prenewparent, struct iatt *postnewparent) +/** + * Wipe the memory used by eliminate path through a + * recursive call + */ +void +wipe_eliminate_path(trash_elim_path **trav) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *dir_name = NULL; - char *tmp_path = NULL; - loc_t tmp_loc = {0,}; - - local = frame->local; - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - dir_name = dirname (tmp_str); - - /* check for the errno, if its ENOENT create directory and call - * rename later - */ - tmp_path = gf_strdup (dir_name); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; - - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, trash_rename_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - - GF_FREE (tmp_str); - return 0; - } - - if ((op_ret == -1) && (op_errno == ENOTDIR)) { - gf_log (this->name, GF_LOG_DEBUG, - "target(%s) exists, cannot keep the dest entry(%s): " - "renaming", local->newpath, local->origpath); - } else if ((op_ret == -1) && (op_errno == EISDIR)) { - gf_log (this->name, GF_LOG_DEBUG, - "target(%s) exists as a directory, cannot keep the " - "copy (%s), renaming", local->newpath, local->origpath); - } + if (trav == NULL) { + return; + } - STACK_WIND (frame, trash_common_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, &local->loc, - &local->newloc); + if (*trav == NULL) { + return; + } - return 0; + wipe_eliminate_path(&(*trav)->next); + GF_FREE((*trav)->path); + GF_FREE(*trav); + *trav = NULL; } - +/** + * This is the call back of rename fop initated using STACK_WIND in + * reconfigure/notify function which is used to rename trash directory + * in the brick when it is required either in volume start or set. + * This frame must destroyed from this function itself since it was + * created by trash xlator + */ int32_t -trash_rename_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *stbuf, struct iatt *preparent, - struct iatt *postparent) +trash_dir_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *tmp_path = NULL; - char *tmp_dirname = NULL; - char *dir_name = NULL; - int32_t count = 0; - loc_t tmp_loc = {0,}; - - local = frame->local; - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - goto out; - } + trash_private_t *priv = NULL; + trash_local_t *local = NULL; - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; + priv = this->private; - tmp_dirname = strchr (tmp_str + count + 1, '/'); + local = frame->local; - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } + if (op_ret == -1) { + gf_log(this->name, GF_LOG_ERROR, + "rename trash directory " + "failed: %s", + strerror(op_errno)); + goto out; + } - tmp_loc.path = tmp_path; + GF_FREE(priv->oldtrash_dir); - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, trash_rename_mkdir_cbk, - tmp_path, this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - } + priv->oldtrash_dir = gf_strdup(priv->newtrash_dir); + if (!priv->oldtrash_dir) { + op_ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + } - goto out; - } - - dir_name = dirname (tmp_str); - if (strcmp ((char*)cookie, dir_name) == 0) { - tmp_loc.path = local->newpath; +out: + frame->local = NULL; + STACK_DESTROY(frame->root); + trash_local_wipe(local); + return op_ret; +} - STACK_WIND (frame, trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->newloc, &tmp_loc); - } +int +rename_trash_directory(xlator_t *this) +{ + trash_private_t *priv = NULL; + int ret = 0; + loc_t loc = { + 0, + }; + loc_t old_loc = { + 0, + }; + call_frame_t *frame = NULL; + trash_local_t *local = NULL; + + priv = this->private; + + frame = create_frame(this, this->ctx->pool); + if (frame == NULL) { + gf_log(this->name, GF_LOG_ERROR, "failed to create frame"); + ret = ENOMEM; + goto out; + } + + local = mem_get0(this->local_pool); + if (!local) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + frame->local = local; + + /* assign new location values to new_loc members */ + gf_uuid_copy(loc.gfid, trash_gfid); + gf_uuid_copy(loc.pargfid, root_gfid); + ret = extract_trash_directory(priv->newtrash_dir, &loc.name); + if (ret) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + loc.path = gf_strdup(priv->newtrash_dir); + if (!loc.path) { + ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + + /* assign old location values to old_loc members */ + gf_uuid_copy(old_loc.gfid, trash_gfid); + gf_uuid_copy(old_loc.pargfid, root_gfid); + ret = extract_trash_directory(priv->oldtrash_dir, &old_loc.name); + if (ret) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + old_loc.path = gf_strdup(priv->oldtrash_dir); + if (!old_loc.path) { + ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + + old_loc.inode = inode_ref(priv->trash_inode); + gf_uuid_copy(old_loc.inode->gfid, old_loc.gfid); + + loc_copy(&local->loc, &old_loc); + loc_copy(&local->newloc, &loc); + + STACK_WIND(frame, trash_dir_rename_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, &old_loc, &loc, NULL); + return 0; out: - GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ - if (tmp_str) - GF_FREE (tmp_str); + if (frame) { + frame->local = NULL; + STACK_DESTROY(frame->root); + } - return 0; + trash_local_wipe(local); + + return ret; } int32_t -trash_rename_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *buf, dict_t *xattr, - struct iatt *postparent) +trash_internal_op_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) { - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - loc_t tmp_loc = {0,}; - - local = frame->local; - priv = this->private; - - if (op_ret == -1) { - STACK_WIND (frame, trash_common_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc, &local->newloc); - return 0; - } - if ((buf->ia_size == 0) || - (buf->ia_size > priv->max_trash_file_size)) { - /* if the file is too big or zero, just unlink it */ - - if (buf->ia_size > priv->max_trash_file_size) { - gf_log (this->name, GF_LOG_DEBUG, - "%s: file size too big (%"PRId64") to " - "move into trash directory", - local->newloc.path, buf->ia_size); - } - - STACK_WIND (frame, trash_common_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc, &local->newloc); - return 0; - } - - tmp_loc.path = local->newpath; - - STACK_WIND (frame, trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->newloc, &tmp_loc); - - return 0; + trash_local_t *local = NULL; + local = frame->local; + + if (op_ret != 0 && !(op_errno == EEXIST)) + gf_log(this->name, GF_LOG_ERROR, + "mkdir failed for " + "internal op directory : %s", + strerror(op_errno)); + + frame->local = NULL; + STACK_DESTROY(frame->root); + trash_local_wipe(local); + return op_ret; } +/** + * This is the call back of mkdir fop initated using STACK_WIND in + * notify/reconfigure function which is used to create trash directory + * in the brick when "trash" is on. The frame of the mkdir must + * destroyed from this function itself since it was created by trash xlator + */ int32_t -trash_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, - loc_t *newloc) +trash_dir_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) { - trash_elim_pattern_t *trav = NULL; - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - struct tm *tm = NULL; - char timestr[256] = {0,}; - time_t utime = 0; - int32_t match = 0; - - priv = this->private; - if (priv->eliminate) { - trav = priv->eliminate; - while (trav) { - if (fnmatch(trav->pattern, newloc->name, 0) == 0) { - match++; - break; - } - trav = trav->next; - } - } + trash_private_t *priv = NULL; + trash_local_t *local = NULL; - if ((strncmp (oldloc->path, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || match) { - /* Trying to rename from the trash dir, - do the actual rename */ - STACK_WIND (frame, trash_common_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - oldloc, newloc); + priv = this->private; - return 0; - } + local = frame->local; - local = GF_CALLOC (1, sizeof (trash_local_t), - gf_trash_mt_trash_local_t); - if (!local) { - gf_log (this->name, GF_LOG_ERROR, "out of memory"); - TRASH_STACK_UNWIND (rename, frame, -1, ENOMEM, - NULL, NULL, NULL, NULL, NULL); - return 0; + if (op_ret == 0) { + priv->oldtrash_dir = gf_strdup(priv->newtrash_dir); + if (!priv->oldtrash_dir) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + op_ret = ENOMEM; } + } else if (op_ret != 0 && errno != EEXIST) + gf_log(this->name, GF_LOG_ERROR, + "mkdir failed for trash" + " directory : %s", + strerror(op_errno)); - frame->local = local; - loc_copy (&local->loc, oldloc); - - loc_copy (&local->newloc, newloc); - - strcpy (local->origpath, newloc->path); - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, newloc->path); - - { - /* append timestamp to file name */ - /* TODO: can we make it optional? */ - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y-%m-%d-%H%M%S", tm); - strcat (local->newpath, timestr); - } + frame->local = NULL; + STACK_DESTROY(frame->root); + trash_local_wipe(local); + return op_ret; +} - /* Send a lookup call on newloc, to ensure we are not - overwriting */ - STACK_WIND (frame, trash_rename_lookup_cbk, - this->children->xlator, - this->children->xlator->fops->lookup, newloc, 0); +/** + * This getxattr calls returns existing trash directory path in + * the dictionary + */ +int32_t +trash_dir_getxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict, + dict_t *xdata) +{ + data_t *data = NULL; + trash_private_t *priv = NULL; + int ret = 0; + trash_local_t *local = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + + data = dict_get(dict, GET_ANCESTRY_PATH_KEY); + if (!data) { + goto out; + } + priv->oldtrash_dir = GF_MALLOC(PATH_MAX, gf_common_mt_char); + if (!priv->oldtrash_dir) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + /* appending '/' if it is not present */ + sprintf(priv->oldtrash_dir, "%s%c", data->data, + data->data[strlen(data->data) - 1] != '/' ? '/' : '\0'); + gf_log(this->name, GF_LOG_DEBUG, + "old trash directory path " + "is %s", + priv->oldtrash_dir); + if (strcmp(priv->newtrash_dir, priv->oldtrash_dir) != 0) { + /* When user set a new name for trash directory, trash + * xlator will perform a rename operation on old trash + * directory to the new one using a STACK_WIND from here. + * This option can be configured only when volume is in + * started state + */ + ret = rename_trash_directory(this); + } +out: + frame->local = NULL; + STACK_DESTROY(frame->root); + trash_local_wipe(local); + return ret; +} +/** + * This is a nameless look up for internal op directory + * The lookup is based on gfid, because internal op directory + * has fixed gfid. + */ +int32_t +trash_internalop_dir_lookup_cbk(call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, + struct iatt *postparent) +{ + trash_private_t *priv = NULL; + int ret = 0; + uuid_t *gfid_ptr = NULL; + loc_t loc = { + 0, + }; + char internal_op_path[PATH_MAX] = { + 0, + }; + dict_t *dict = NULL; + trash_local_t *local = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + if (op_ret != 0 && op_errno == ENOENT) { + loc_wipe(&local->loc); + gfid_ptr = GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t); + if (!gfid_ptr) { + ret = ENOMEM; + goto out; + } + + gf_uuid_copy(*gfid_ptr, internal_op_gfid); + + dict = dict_new(); + if (!dict) { + ret = ENOMEM; + goto out; + } + ret = dict_set_gfuuid(dict, "gfid-req", *gfid_ptr, false); + if (ret) { + gf_log(this->name, GF_LOG_ERROR, "setting key gfid-req failed"); + goto out; + } + gf_uuid_copy(loc.gfid, internal_op_gfid); + gf_uuid_copy(loc.pargfid, trash_gfid); + + loc.inode = inode_new(priv->trash_itable); + + /* The mkdir call for creating internal op directory */ + loc.name = gf_strdup("internal_op"); + if (!loc.name) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + sprintf(internal_op_path, "%s%s/", priv->newtrash_dir, loc.name); + + loc.path = gf_strdup(internal_op_path); + if (!loc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + + loc_copy(&local->loc, &loc); + STACK_WIND(frame, trash_internal_op_mkdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &loc, 0755, 0022, dict); return 0; + } + +out: + if (ret && gfid_ptr) + GF_FREE(gfid_ptr); + if (dict) + dict_unref(dict); + frame->local = NULL; + STACK_DESTROY(frame->root); + trash_local_wipe(local); + return op_ret; } +/** + * This is a nameless look up for old trash directory + * The lookup is based on gfid, because trash directory + * has fixed gfid. + */ int32_t -trash_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) +trash_dir_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, struct iatt *postparent) { - trash_elim_pattern_t *trav = NULL; - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - struct tm *tm = NULL; - char timestr[256] = {0,}; - time_t utime = 0; - int32_t match = 0; + trash_private_t *priv = NULL; + loc_t loc = { + 0, + }; + int ret = 0; + uuid_t *gfid_ptr = NULL; + dict_t *dict = NULL; + trash_local_t *local = NULL; - priv = this->private; + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); - if (priv->eliminate) { - trav = priv->eliminate; - while (trav) { - if (fnmatch(trav->pattern, loc->name, 0) == 0) { - match++; - break; - } - trav = trav->next; - } - } + local = frame->local; - if ((strncmp (loc->path, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || (match)) { - if (match) { - gf_log (this->name, GF_LOG_DEBUG, - "%s: file matches eliminate pattern, " - "not moved to trash", loc->name); - } else { - /* unlink from the trash-dir, not keeping any copy */ - ; - } - - STACK_WIND (frame, trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, loc); - return 0; - } + loc_wipe(&local->loc); + if (op_ret == 0) { + gf_log(this->name, GF_LOG_DEBUG, "inode found with gfid %s", + uuid_utoa(buf->ia_gfid)); - local = GF_CALLOC (1, sizeof (trash_local_t), - gf_trash_mt_trash_local_t); - if (!local) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - TRASH_STACK_UNWIND (unlink, frame, -1, ENOMEM, NULL, NULL); - return 0; - } - frame->local = local; - loc_copy (&local->loc, loc); - - strcpy (local->origpath, loc->path); - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, loc->path); - - { - /* append timestamp to file name */ - /* TODO: can we make it optional? */ - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y-%m-%d-%H%M%S", tm); - strcat (local->newpath, timestr); - } + gf_uuid_copy(loc.gfid, trash_gfid); - LOCK_INIT (&frame->lock); + /* Find trash inode using available information */ + priv->trash_inode = inode_link(inode, NULL, NULL, buf); - STACK_WIND (frame, trash_unlink_stat_cbk, - this->children->xlator, - this->children->xlator->fops->stat, loc); + loc.inode = inode_ref(priv->trash_inode); + loc_copy(&local->loc, &loc); + /*Used to find path of old trash directory*/ + STACK_WIND(frame, trash_dir_getxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->getxattr, &loc, + GET_ANCESTRY_PATH_KEY, xdata); + return 0; + } + + /* If there is no old trash directory we set its value to new one, + * which is the valid condition for trash directory creation + */ + else { + gf_log(this->name, GF_LOG_DEBUG, + "Creating trash " + "directory %s ", + priv->newtrash_dir); + + gfid_ptr = GF_MALLOC(sizeof(uuid_t), gf_common_mt_uuid_t); + if (!gfid_ptr) { + ret = ENOMEM; + goto out; + } + gf_uuid_copy(*gfid_ptr, trash_gfid); + + gf_uuid_copy(loc.gfid, trash_gfid); + gf_uuid_copy(loc.pargfid, root_gfid); + ret = extract_trash_directory(priv->newtrash_dir, &loc.name); + if (ret) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + loc.path = gf_strdup(priv->newtrash_dir); + if (!loc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + + priv->trash_inode = inode_new(priv->trash_itable); + priv->trash_inode->ia_type = IA_IFDIR; + loc.inode = inode_ref(priv->trash_inode); + dict = dict_new(); + if (!dict) { + ret = ENOMEM; + goto out; + } + /* Fixed gfid is set for trash directory with + * this function + */ + ret = dict_set_gfuuid(dict, "gfid-req", *gfid_ptr, false); + if (ret) { + gf_log(this->name, GF_LOG_ERROR, "setting key gfid-req failed"); + goto out; + } + loc_copy(&local->loc, &loc); + + /* The mkdir call for creating trash directory */ + STACK_WIND(frame, trash_dir_mkdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &loc, 0755, 0022, dict); return 0; + } +out: + if (ret && gfid_ptr) + GF_FREE(gfid_ptr); + if (dict) + dict_unref(dict); + frame->local = NULL; + STACK_DESTROY(frame->root); + trash_local_wipe(local); + return ret; } -int32_t -trash_truncate_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *preparent, struct iatt *postparent) +int +create_or_rename_trash_directory(xlator_t *this) { - /* use this Function when a failure occurs, and - delete the newly created file. */ - trash_local_t *local = NULL; - - local = frame->local; - - if (op_ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "deleting the newly created file: %s", - strerror (op_errno)); - } + trash_private_t *priv = NULL; + int ret = 0; + loc_t loc = { + 0, + }; + call_frame_t *frame = NULL; + trash_local_t *local = NULL; + + priv = this->private; + + frame = create_frame(this, this->ctx->pool); + if (frame == NULL) { + gf_log(this->name, GF_LOG_ERROR, "failed to create frame"); + ret = ENOMEM; + goto out; + } + + local = mem_get0(this->local_pool); + if (!local) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + frame->local = local; + + loc.inode = inode_new(priv->trash_itable); + gf_uuid_copy(loc.gfid, trash_gfid); + loc_copy(&local->loc, &loc); + gf_log(this->name, GF_LOG_DEBUG, + "nameless lookup for" + "old trash directory"); + STACK_WIND(frame, trash_dir_lookup_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, &loc, NULL); +out: + return ret; +} - STACK_WIND (frame, trash_common_unwind_buf_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, - &local->loc, local->fop_offset); +int +create_internalop_directory(xlator_t *this) +{ + trash_private_t *priv = NULL; + int ret = 0; + loc_t loc = { + 0, + }; + call_frame_t *frame = NULL; + trash_local_t *local = NULL; + + priv = this->private; + + frame = create_frame(this, this->ctx->pool); + if (frame == NULL) { + gf_log(this->name, GF_LOG_ERROR, "failed to create frame"); + ret = ENOMEM; + goto out; + } + + local = mem_get0(this->local_pool); + if (!local) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + frame->local = local; + + gf_uuid_copy(loc.gfid, internal_op_gfid); + gf_uuid_copy(loc.pargfid, trash_gfid); + loc.inode = inode_new(priv->trash_itable); + loc.inode->ia_type = IA_IFDIR; + + loc_copy(&local->loc, &loc); + STACK_WIND(frame, trash_internalop_dir_lookup_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, &loc, NULL); +out: - return 0; + return ret; } int32_t -trash_truncate_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iovec *vector, int32_t count, - struct iatt *stbuf, struct iobref *iobuf) +trash_common_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) { - trash_local_t *local = NULL; + STACK_UNWIND_STRICT(mkdir, frame, op_ret, op_errno, inode, buf, preparent, + postparent, xdata); + return 0; +} - local = frame->local; +int32_t +trash_common_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) +{ + STACK_UNWIND_STRICT(rename, frame, op_ret, op_errno, buf, preoldparent, + postoldparent, prenewparent, postnewparent, xdata); + return 0; +} - if (op_ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "readv on the existing file failed: %s", - strerror (op_errno)); +int32_t +trash_common_rmdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + STACK_UNWIND_STRICT(rmdir, frame, op_ret, op_errno, preparent, postparent, + xdata); + return 0; +} - STACK_WIND (frame, trash_truncate_unlink_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, - &local->newloc); - goto out; - } +/** + * move backs from trash translator to unlink call + */ +int32_t +trash_common_unwind_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) +{ + TRASH_STACK_UNWIND(unlink, frame, op_ret, op_errno, preparent, postparent, + xdata); + return 0; +} - local->fsize = stbuf->ia_size; - STACK_WIND (frame, trash_truncate_writev_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->writev, - local->newfd, vector, count, local->cur_offset, iobuf); +/** + * If the path is not present in the trash directory,it will recursively + * call this call-back and one by one directories will be created from + * the starting + */ +int32_t +trash_unlink_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *stbuf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + trash_local_t *local = NULL; + char *tmp_str = NULL; + char *tmp_path = NULL; + char *tmp_dirname = NULL; + char *tmp_stat = NULL; + char real_path[PATH_MAX] = { + 0, + }; + char *dir_name = NULL; + size_t count = 0; + int32_t loop_count = 0; + int i = 0; + loc_t tmp_loc = { + 0, + }; + trash_private_t *priv = NULL; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + TRASH_UNSET_PID(frame, local); + + tmp_str = gf_strdup(local->newpath); + if (!tmp_str) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = -1; + goto out; + } + loop_count = local->loop_count; + + /* The directory is not present , need to create it */ + if ((op_ret == -1) && (op_errno == ENOENT)) { + tmp_dirname = strchr(tmp_str, '/'); + while (tmp_dirname) { + count = tmp_dirname - tmp_str; + if (count == 0) + count = 1; + i++; + if (i > loop_count) + break; + tmp_dirname = strchr(tmp_str + count + 1, '/'); + } + tmp_path = gf_memdup(local->newpath, count + 1); + if (!tmp_path) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + tmp_path[count] = '\0'; + + loc_copy(&tmp_loc, &local->loc); + tmp_loc.path = gf_strdup(tmp_path); + if (!tmp_loc.path) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + + /* Stores the the name of directory to be created */ + tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1); + if (!tmp_loc.name) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + + remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat); + if (tmp_stat) + strncat(real_path, tmp_stat, + sizeof(real_path) - strlen(real_path) - 1); + + TRASH_SET_PID(frame, local); + + STACK_WIND_COOKIE(frame, trash_unlink_mkdir_cbk, tmp_path, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, get_permission(real_path), 0022, xdata); + loc_wipe(&tmp_loc); + goto out; + } + + /* Given path is created , comparing to the required path */ + if (op_ret == 0) { + dir_name = dirname(tmp_str); + if (strcmp((char *)cookie, dir_name) == 0) { + /* File path exists we can rename it*/ + loc_copy(&tmp_loc, &local->loc); + tmp_loc.path = local->newpath; + STACK_WIND(frame, trash_unlink_rename_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, &local->loc, &tmp_loc, + xdata); + goto out; + } + } + + if ((op_ret == -1) && (op_errno != EEXIST)) { + gf_log(this->name, GF_LOG_ERROR, + "Directory creation failed [%s]. " + "Therefore unlinking %s without moving to trash " + "directory", + strerror(op_errno), local->loc.name); + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata); + goto out; + } + + LOCK(&frame->lock); + { + loop_count = ++local->loop_count; + } + UNLOCK(&frame->lock); + + tmp_dirname = strchr(tmp_str, '/'); + + /* Path is not completed , need to create remaining path */ + while (tmp_dirname) { + count = tmp_dirname - tmp_str; + if (count == 0) + count = 1; + i++; + if (i > loop_count) + break; + tmp_dirname = strchr(tmp_str + count + 1, '/'); + } + tmp_path = gf_memdup(local->newpath, count + 1); + if (!tmp_path) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = -1; + goto out; + } + tmp_path[count] = '\0'; + + loc_copy(&tmp_loc, &local->loc); + tmp_loc.path = gf_strdup(tmp_path); + if (!tmp_loc.path) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = -1; + goto out; + } + + /* Stores the the name of directory to be created */ + tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1); + if (!tmp_loc.name) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = -1; + goto out; + } + + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + + remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat); + if (tmp_stat) + strncat(real_path, tmp_stat, sizeof(real_path) - strlen(real_path) - 1); + + TRASH_SET_PID(frame, local); + + STACK_WIND_COOKIE(frame, trash_unlink_mkdir_cbk, tmp_path, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, get_permission(real_path), 0022, xdata); out: - return 0; - + if (tmp_path) + GF_FREE(tmp_path); + if (tmp_str) + GF_FREE(tmp_str); + return ret; } +/** + * The name of unlinking file should be renamed as starting + * from trash directory as mentioned in the mount point + */ int32_t -trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *prebuf, struct iatt *postbuf) +trash_unlink_rename_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) { - trash_local_t *local = NULL; - - local = frame->local; - - if (op_ret == -1) { - /* Let truncate work, but previous copy is not preserved. */ - gf_log (this->name, GF_LOG_DEBUG, - "writev on the existing file failed: %s", - strerror (op_errno)); - - STACK_WIND (frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->unlink, &local->newloc); - goto out; - } - - if (local->cur_offset < local->fsize) { - local->cur_offset += GF_BLOCK_READV_SIZE; - /* Loop back and Read the contents again. */ - STACK_WIND (frame, trash_truncate_readv_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->readv, - local->fd, (size_t)GF_BLOCK_READV_SIZE, - local->cur_offset); - goto out; - } - - - /* OOFH.....Finally calling Truncate. */ - STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->truncate, &local->loc, - local->fop_offset); - + trash_local_t *local = NULL; + trash_private_t *priv = NULL; + char *tmp_str = NULL; + char *dir_name = NULL; + char *tmp_cookie = NULL; + loc_t tmp_loc = { + 0, + }; + dict_t *new_xdata = NULL; + char *tmp_stat = NULL; + char real_path[PATH_MAX] = { + 0, + }; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + if ((op_ret == -1) && (op_errno == ENOENT)) { + /* the file path does not exist we want to create path + * for the file + */ + tmp_str = gf_strdup(local->newpath); + if (!tmp_str) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + dir_name = dirname(tmp_str); /* stores directory name */ + + loc_copy(&tmp_loc, &local->loc); + tmp_loc.path = gf_strdup(dir_name); + if (!tmp_loc.path) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + + tmp_cookie = gf_strdup(dir_name); + if (!tmp_cookie) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + remove_trash_path(tmp_str, (frame->root->pid < 0), &tmp_stat); + if (tmp_stat) + strncat(real_path, tmp_stat, + sizeof(real_path) - strlen(real_path) - 1); + + TRASH_SET_PID(frame, local); + + /* create the directory with proper permissions */ + STACK_WIND_COOKIE(frame, trash_unlink_mkdir_cbk, tmp_cookie, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, get_permission(real_path), 0022, xdata); + loc_wipe(&tmp_loc); + goto out; + } + + if ((op_ret == -1) && (op_errno == ENOTDIR)) { + /* if entry is already present in trash directory, + * new one is not copied*/ + gf_log(this->name, GF_LOG_DEBUG, + "target(%s) exists, cannot keep the copy, deleting", + local->newpath); + + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata); + + goto out; + } + + if ((op_ret == -1) && (op_errno == EISDIR)) { + /* if entry is directory,we remove directly */ + gf_log(this->name, GF_LOG_DEBUG, + "target(%s) exists as directory, cannot keep copy, " + "deleting", + local->newpath); + + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata); + goto out; + } + + /********************************************************************** + * + * CTR Xlator message handling done here! + * + **********************************************************************/ + /** + * If unlink is handled by trash translator, it should inform the + * CTR Xlator. And trash translator only handles the unlink for + * the last hardlink. + * + * Check if there is a GF_REQUEST_LINK_COUNT_XDATA from CTR Xlator + * + */ + + if (local->ctr_link_count_req) { + /* Sending back inode link count to ctr_unlink + * (changetimerecoder xlator) via + * "GF_RESPONSE_LINK_COUNT_XDATA" key using xdata. + * */ + if (xdata) { + ret = dict_set_uint32(xdata, GF_RESPONSE_LINK_COUNT_XDATA, 1); + if (ret == -1) { + gf_log(this->name, GF_LOG_WARNING, + "Failed to set" + " GF_RESPONSE_LINK_COUNT_XDATA"); + } + } else { + new_xdata = dict_new(); + if (!new_xdata) { + gf_log(this->name, GF_LOG_WARNING, + "Memory allocation failure while " + "creating new_xdata"); + goto ctr_out; + } + ret = dict_set_uint32(new_xdata, GF_RESPONSE_LINK_COUNT_XDATA, 1); + if (ret == -1) { + gf_log(this->name, GF_LOG_WARNING, + "Failed to set" + " GF_RESPONSE_LINK_COUNT_XDATA"); + } + ctr_out: + TRASH_STACK_UNWIND(unlink, frame, 0, op_errno, preoldparent, + postoldparent, new_xdata); + goto out; + } + } + /* All other cases, unlink should return success */ + TRASH_STACK_UNWIND(unlink, frame, 0, op_errno, preoldparent, postoldparent, + xdata); out: - return 0; -} + if (tmp_str) + GF_FREE(tmp_str); + if (tmp_cookie) + GF_FREE(tmp_cookie); + if (new_xdata) + dict_unref(new_xdata); + return ret; +} +/** + * move backs from trash translator to truncate call + */ int32_t -trash_truncate_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, fd_t *fd) +trash_common_unwind_buf_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) { - trash_local_t *local = NULL; - - local = frame->local; - - if (op_ret == -1) { - //Let truncate work, but previous copy is not preserved. - gf_log (this->name, GF_LOG_DEBUG, - "open on the existing file failed: %s", - strerror (op_errno)); + TRASH_STACK_UNWIND(truncate, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + return 0; +} - STACK_WIND (frame, trash_truncate_unlink_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, - &local->newloc); - goto out; - } +int32_t +trash_unlink_stat_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) +{ + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + loc_t new_loc = { + 0, + }; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + if (op_ret == -1) { + gf_log(this->name, GF_LOG_DEBUG, "%s: %s", local->loc.path, + strerror(op_errno)); + TRASH_STACK_UNWIND(unlink, frame, op_ret, op_errno, buf, NULL, xdata); + ret = -1; + goto out; + } + + /* Only last hardlink will be moved to trash directory */ + if (buf->ia_nlink > 1) { + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata); + goto out; + } + + /* if the file is too big just unlink it */ + if (buf->ia_size > (priv->max_trash_file_size)) { + gf_log(this->name, GF_LOG_DEBUG, + "%s: file size too big (%" PRId64 + ") to " + "move into trash directory", + local->loc.path, buf->ia_size); + + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->loc, 0, xdata); + goto out; + } + + /* Copies new path for renaming */ + loc_copy(&new_loc, &local->loc); + new_loc.path = gf_strdup(local->newpath); + if (!new_loc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + + STACK_WIND(frame, trash_unlink_rename_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, &local->loc, &new_loc, xdata); - local->cur_offset = local->fop_offset; +out: + loc_wipe(&new_loc); - STACK_WIND (frame, trash_truncate_readv_cbk, - FIRST_CHILD (this), FIRST_CHILD (this)->fops->readv, - local->fd, (size_t)GF_BLOCK_READV_SIZE, local->cur_offset); + return ret; +} +/** + * Unlink is called internally by rm system call and also + * by internal operations of gluster such as self-heal + */ +int32_t +trash_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflags, + dict_t *xdata) +{ + trash_private_t *priv = NULL; + trash_local_t *local = NULL; /* files inside trash */ + int32_t match = 0; + int32_t ctr_link_req = 0; + char *pathbuf = NULL; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + /* If trash is not active or not enabled through cli, then + * we bypass and wind back + */ + if (!priv->state) { + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, 0, xdata); + goto out; + } + + /* The files removed by gluster internal operations such as self-heal, + * should moved to trash directory , but files by client should not + * moved + */ + if ((frame->root->pid < 0) && !priv->internal) { + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, 0, xdata); + goto out; + } + /* loc need some gfid which will be present in inode */ + gf_uuid_copy(loc->gfid, loc->inode->gfid); + + /* Checking for valid location */ + if (gf_uuid_is_null(loc->gfid) && gf_uuid_is_null(loc->inode->gfid)) { + gf_log(this->name, GF_LOG_DEBUG, "Bad address"); + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, 0, xdata); + ret = EFAULT; + goto out; + } + + /* This will be more accurate */ + inode_path(loc->inode, NULL, &pathbuf); + /* Check whether the file is present under eliminate paths or + * inside trash directory. In both cases we don't need to move the + * file to trash directory. Instead delete it permanently + */ + match = check_whether_eliminate_path(priv->eliminate, pathbuf); + if ((strncmp(pathbuf, priv->newtrash_dir, strlen(priv->newtrash_dir)) == + 0) || + (match)) { + if (match) { + gf_log(this->name, GF_LOG_DEBUG, + "%s is a file comes under an eliminate path, " + "so it is not moved to trash", + loc->name); + } + + /* Trying to unlink from the trash-dir. So do the + * actual unlink without moving to trash-dir. + */ + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, 0, xdata); + goto out; + } + + local = mem_get0(this->local_pool); + if (!local) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + TRASH_STACK_UNWIND(unlink, frame, -1, ENOMEM, NULL, NULL, xdata); + ret = ENOMEM; + goto out; + } + frame->local = local; + loc_copy(&local->loc, loc); + + /* rename new location of file as starting from trash directory */ + copy_trash_path(priv->newtrash_dir, (frame->root->pid < 0), local->newpath, + sizeof(local->newpath)); + strncat(local->newpath, pathbuf, + sizeof(local->newpath) - strlen(local->newpath) - 1); + + /* append timestamp to file name so that we can avoid + * name collisions inside trash + */ + append_time_stamp(local->newpath, sizeof(local->newpath)); + if (strlen(local->newpath) > PATH_MAX) { + STACK_WIND(frame, trash_common_unwind_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, 0, xdata); + goto out; + } + + /* To know whether CTR xlator requested for the link count */ + ret = dict_get_int32(xdata, GF_REQUEST_LINK_COUNT_XDATA, &ctr_link_req); + if (ret) { + local->ctr_link_count_req = _gf_false; + ret = 0; + } else + local->ctr_link_count_req = _gf_true; + + LOCK_INIT(&frame->lock); + + STACK_WIND(frame, trash_unlink_stat_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->stat, loc, xdata); out: - return 0; + return ret; } - +/** + * Use this when a failure occurs, and delete the newly created file + */ int32_t -trash_truncate_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, fd_t *fd, - inode_t *inode, struct iatt *buf, - struct iatt *preparent, struct iatt *postparent) +trash_truncate_unlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *dir_name = NULL; - char *tmp_path = NULL; - int32_t flags = 0; - loc_t tmp_loc = {0,}; - - local = frame->local; - - if ((op_ret == -1) && (op_errno == ENOENT)) { - //Creating the directory structure here. - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - dir_name = dirname (tmp_str); - - tmp_path = gf_strdup (dir_name); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; - - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, - tmp_path, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->mkdir, - &tmp_loc, 0755, NULL); - GF_FREE (tmp_str); - goto out; - } - - if (op_ret == -1) { - //Let truncate work, but previous copy is not preserved. - //Deleting the newly created copy. - gf_log (this->name, GF_LOG_DEBUG, - "creation of new file in trash-dir failed, " - "when truncate was called: %s", strerror (op_errno)); - - STACK_WIND (frame, trash_common_unwind_buf_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->truncate, &local->loc, - local->fop_offset); - goto out; - } + trash_local_t *local = NULL; - flags = O_RDONLY; + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); - local->fd = fd_create (local->loc.inode, frame->root->pid); + if (op_ret == -1) { + gf_log(this->name, GF_LOG_DEBUG, "deleting the newly created file: %s", + strerror(op_errno)); + } - STACK_WIND (frame, trash_truncate_open_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->open, &local->loc, flags, - local->fd, 0); + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); out: - return 0; + return 0; } +/** + * Read from source file + */ int32_t -trash_truncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *stbuf, struct iatt *preparent, - struct iatt *postparent) +trash_truncate_readv_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iovec *vector, + int32_t count, struct iatt *stbuf, + struct iobref *iobuf, dict_t *xdata) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *tmp_path = NULL; - char *tmp_dirname = NULL; - char *dir_name = NULL; - int32_t count = 0; - int32_t flags = 0; - int32_t loop_count = 0; - int i = 0; - loc_t tmp_loc = {0,}; - - local = frame->local; - if (!local) - goto out; - - loop_count = local->loop_count; - - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - goto out; - } - - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - i++; - if (i > loop_count) - break; - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; - STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, - tmp_path, this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - - goto out; - } + trash_local_t *local = NULL; - if (op_ret == 0) { - dir_name = dirname (tmp_str); - if (strcmp ((char*)cookie, dir_name) == 0) { - flags = O_CREAT|O_EXCL|O_WRONLY; - ia_prot_t prot = {0, }; - - //Call create again once directory structure is created. - STACK_WIND (frame, trash_truncate_create_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, - &local->newloc, flags, - st_mode_from_ia (prot, local->loc.inode->ia_type), - local->newfd, NULL); - goto out; - } - } + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); - LOCK (&frame->lock); - { - loop_count = ++local->loop_count; - } - UNLOCK (&frame->lock); + if (op_ret == -1) { + gf_log(this->name, GF_LOG_DEBUG, + "readv on the existing file failed: %s", strerror(op_errno)); - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - - i++; - if ((i > loop_count) || (count > PATH_MAX)) - break; - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; + STACK_WIND(frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, xdata); + goto out; + } - STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); + local->fsize = stbuf->ia_size; + STACK_WIND(frame, trash_truncate_writev_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, local->newfd, vector, count, + local->cur_offset, 0, iobuf, xdata); out: - GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ - if (tmp_str) - GF_FREE (tmp_str); - - return 0; + return 0; } - +/** + * Write to file created in trash directory + */ int32_t -trash_truncate_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf) +trash_truncate_writev_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) { - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - struct tm *tm = NULL; - char timestr[256] = {0,}; - char loc_newname[PATH_MAX] = {0,}; - time_t utime = 0; - int32_t flags = 0; - - priv = this->private; - local = frame->local; - - if (op_ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "fstat on the file failed: %s", - strerror (op_errno)); - - TRASH_STACK_UNWIND (truncate, frame, op_ret, op_errno, buf, NULL); - return 0; - } - - if ((buf->ia_size == 0) || (buf->ia_size > priv->max_trash_file_size)) { - // If the file is too big, just unlink it. - if (buf->ia_size > priv->max_trash_file_size) - gf_log (this->name, GF_LOG_DEBUG, "%s: file too big, " - "not moving to trash", local->loc.path); - - STACK_WIND (frame, trash_common_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->truncate, - &local->loc, local->fop_offset); - return 0; - } - - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, local->loc.path); - - { - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y-%m-%d-%H%M%S", tm); - strcat (local->newpath, timestr); - } - strcpy (loc_newname,local->loc.name); - strcat (loc_newname,timestr); - - local->newloc.name = gf_strdup (loc_newname); - local->newloc.path = gf_strdup (local->newpath); - local->newloc.inode = inode_new (local->loc.inode->table); - local->newfd = fd_create (local->newloc.inode, frame->root->pid); + trash_local_t *local = NULL; + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + if (op_ret == -1) { + /* Let truncate work, but previous copy is not preserved. */ + gf_log(this->name, GF_LOG_DEBUG, + "writev on the existing file failed: %s", strerror(op_errno)); + + STACK_WIND(frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, xdata); + goto out; + } + + if (local->cur_offset < local->fsize) { + local->cur_offset += GF_BLOCK_READV_SIZE; + /* Loop back and Read the contents again. */ + STACK_WIND(frame, trash_truncate_readv_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readv, local->fd, + (size_t)GF_BLOCK_READV_SIZE, local->cur_offset, 0, xdata); + goto out; + } + + /* OOFH.....Finally calling Truncate. */ + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); - flags = O_CREAT|O_EXCL|O_WRONLY; - - STACK_WIND (frame, trash_truncate_create_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->create, - &local->newloc, flags, - st_mode_from_ia (buf->ia_prot, local->loc.inode->ia_type), - local->newfd, NULL); - - return 0; +out: + return 0; } +/** + * The source file is opened for reading and writing + */ int32_t -trash_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, - off_t offset) +trash_truncate_open_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, + dict_t *xdata) { - trash_elim_pattern_t *trav = NULL; - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - int32_t match = 0; + trash_local_t *local = NULL; - priv = this->private; - if (priv->eliminate) { - trav = priv->eliminate; - while (trav) { - if (fnmatch(trav->pattern, loc->name, 0) == 0) { - match++; - break; - } - trav = trav->next; - } - } + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); - if ((strncmp (loc->path, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || (offset) || (match)) { - if (match) { - gf_log (this->name, GF_LOG_DEBUG, - "%s: file not moved to trash as per option " - "'eliminate'", loc->path); - } - - // Trying to truncate from the trash can dir, - // do the actual truncate without moving to trash-dir. - STACK_WIND (frame, trash_common_unwind_buf_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->truncate, loc, offset); - goto out; - } - - LOCK_INIT (&frame->lock); - - local = GF_CALLOC (1, sizeof (trash_local_t), - gf_trash_mt_trash_local_t); - if (!local) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - TRASH_STACK_UNWIND (truncate, frame, -1, ENOMEM, NULL, NULL); - return 0; - } + if (op_ret == -1) { + /* Let truncate work, but previous copy is not preserved. */ + gf_log(this->name, GF_LOG_DEBUG, "open on the existing file failed: %s", + strerror(op_errno)); - loc_copy (&local->loc, loc); + STACK_WIND(frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, xdata); + goto out; + } - local->fop_offset = offset; + fd_bind(fd); - frame->local = local; + local->cur_offset = 0; - STACK_WIND (frame, trash_truncate_stat_cbk, - this->children->xlator, - this->children->xlator->fops->stat, loc); + STACK_WIND(frame, trash_truncate_readv_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readv, local->fd, + (size_t)GF_BLOCK_READV_SIZE, local->cur_offset, 0, xdata); out: - return 0; + return 0; } +/** + * Creates new file descriptor for read and write operations, + * if the path is present in trash directory + */ int32_t -trash_ftruncate_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *preparent, struct iatt *postparent) +trash_truncate_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, fd_t *fd, + inode_t *inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) { - trash_local_t *local = NULL; - - local = frame->local; - - if (op_ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "%s: failed to unlink new file: %s", - local->newloc.path, strerror(op_errno)); - + trash_local_t *local = NULL; + char *tmp_str = NULL; + char *dir_name = NULL; + char *tmp_path = NULL; + int32_t flags = 0; + loc_t tmp_loc = { + 0, + }; + char *tmp_stat = NULL; + char real_path[PATH_MAX] = { + 0, + }; + trash_private_t *priv = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + TRASH_UNSET_PID(frame, local); + + /* Checks whether path is present in trash directory or not */ + + if ((op_ret == -1) && (op_errno == ENOENT)) { + /* Creating the directory structure here. */ + tmp_str = gf_strdup(local->newpath); + if (!tmp_str) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; } + dir_name = dirname(tmp_str); - STACK_WIND (frame, trash_common_unwind_buf_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->ftruncate, - local->fd, local->fop_offset); + tmp_path = gf_strdup(dir_name); + if (!tmp_path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + loc_copy(&tmp_loc, &local->newloc); + tmp_loc.path = gf_strdup(tmp_path); + if (!tmp_loc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat); + if (tmp_stat) + strncat(real_path, tmp_stat, + sizeof(real_path) - strlen(real_path) - 1); + + TRASH_SET_PID(frame, local); + + /* create the directory with proper permissions */ + STACK_WIND_COOKIE(frame, trash_truncate_mkdir_cbk, tmp_path, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, get_permission(real_path), 0022, xdata); + loc_wipe(&tmp_loc); + goto out; + } + + if (op_ret == -1) { + /* Let truncate work, but previous copy is not preserved. + * Deleting the newly created copy. + */ + gf_log(this->name, GF_LOG_DEBUG, + "creation of new file in trash-dir failed, " + "when truncate was called: %s", + strerror(op_errno)); + + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); + goto out; + } + + fd_bind(fd); + flags = O_RDONLY; + + /* fd which represents source file for reading and writing from it */ + + local->fd = fd_create(local->loc.inode, frame->root->pid); + + STACK_WIND(frame, trash_truncate_open_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->open, &local->loc, flags, local->fd, 0); +out: + if (tmp_str) + GF_FREE(tmp_str); + if (tmp_path) + GF_FREE(tmp_path); - return 0; + return 0; } +/** + * If the path is not present in the trash directory,it will recursively call + * this call-back and one by one directories will be created from the + * beginning + */ int32_t -trash_ftruncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iatt *prebuf, struct iatt *postbuf) +trash_truncate_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *stbuf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) { - trash_local_t *local = NULL; - - local = frame->local; - - if (op_ret == -1) { - STACK_WIND (frame, trash_ftruncate_unlink_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, - &local->newloc); - return 0; - } - - if (local->cur_offset < local->fsize) { - local->cur_offset += GF_BLOCK_READV_SIZE; - STACK_WIND (frame, trash_ftruncate_readv_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->readv, - local->fd, (size_t)GF_BLOCK_READV_SIZE, - local->cur_offset); - return 0; - } + trash_local_t *local = NULL; + trash_private_t *priv = NULL; + char *tmp_str = NULL; + char *tmp_path = NULL; + char *tmp_dirname = NULL; + char *dir_name = NULL; + char *tmp_stat = NULL; + char real_path[PATH_MAX] = { + 0, + }; + size_t count = 0; + int32_t flags = 0; + int32_t loop_count = 0; + int i = 0; + loc_t tmp_loc = { + 0, + }; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + loop_count = local->loop_count; + + TRASH_UNSET_PID(frame, local); + + tmp_str = gf_strdup(local->newpath); + if (!tmp_str) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + + if ((op_ret == -1) && (op_errno == ENOENT)) { + tmp_dirname = strchr(tmp_str, '/'); + while (tmp_dirname) { + count = tmp_dirname - tmp_str; + if (count == 0) + count = 1; + i++; + if (i > loop_count) + break; + tmp_dirname = strchr(tmp_str + count + 1, '/'); + } + tmp_path = gf_memdup(local->newpath, count + 1); + if (!tmp_path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + tmp_path[count] = '\0'; + + loc_copy(&tmp_loc, &local->newloc); + tmp_loc.path = gf_strdup(tmp_path); + if (!tmp_loc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + + /* Stores the the name of directory to be created */ + tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1); + if (!tmp_loc.name) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat); + if (tmp_stat) + strncat(real_path, tmp_stat, + sizeof(real_path) - strlen(real_path) - 1); + + TRASH_SET_PID(frame, local); + + STACK_WIND_COOKIE(frame, trash_truncate_mkdir_cbk, tmp_path, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, get_permission(real_path), 0022, xdata); + loc_wipe(&tmp_loc); + goto out; + } + + if (op_ret == 0) { + dir_name = dirname(tmp_str); + if (strcmp((char *)cookie, dir_name) == 0) { + flags = O_CREAT | O_EXCL | O_WRONLY; + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + strncat(real_path, local->origpath, + sizeof(real_path) - strlen(real_path) - 1); + /* Call create again once directory structure + is created. */ + + TRASH_SET_PID(frame, local); + + STACK_WIND(frame, trash_truncate_create_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->create, &local->newloc, flags, + get_permission(real_path), 0022, local->newfd, xdata); + goto out; + } + } + + if ((op_ret == -1) && (op_errno != EEXIST)) { + gf_log(this->name, GF_LOG_ERROR, + "Directory creation failed [%s]. " + "Therefore truncating %s without moving the " + "original copy to trash directory", + strerror(op_errno), local->loc.name); + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); + goto out; + } + + LOCK(&frame->lock); + { + loop_count = ++local->loop_count; + } + UNLOCK(&frame->lock); + + tmp_dirname = strchr(tmp_str, '/'); + while (tmp_dirname) { + count = tmp_dirname - tmp_str; + if (count == 0) + count = 1; + i++; + if (i > loop_count) + break; + tmp_dirname = strchr(tmp_str + count + 1, '/'); + } + tmp_path = gf_memdup(local->newpath, count + 1); + if (!tmp_path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + tmp_path[count] = '\0'; + + loc_copy(&tmp_loc, &local->newloc); + tmp_loc.path = gf_strdup(tmp_path); + if (!tmp_loc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + + /* Stores the the name of directory to be created */ + tmp_loc.name = gf_strdup(strrchr(tmp_path, '/') + 1); + if (!tmp_loc.name) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + + strncpy(real_path, priv->brick_path, sizeof(real_path)); + real_path[sizeof(real_path) - 1] = 0; + remove_trash_path(tmp_path, (frame->root->pid < 0), &tmp_stat); + if (tmp_stat) + strncat(real_path, tmp_stat, sizeof(real_path) - strlen(real_path) - 1); + + TRASH_SET_PID(frame, local); + + STACK_WIND_COOKIE(frame, trash_truncate_mkdir_cbk, tmp_path, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + &tmp_loc, get_permission(real_path), 0022, xdata); - STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->ftruncate, local->fd, - local->fop_offset); +out: + if (tmp_str) + GF_FREE(tmp_str); + if (tmp_path) + GF_FREE(tmp_path); - return 0; + return ret; } - int32_t -trash_ftruncate_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, - struct iovec *vector, int32_t count, - struct iatt *stbuf, struct iobref *iobuf) +trash_truncate_stat_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) { - trash_local_t *local = NULL; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + char loc_newname[PATH_MAX] = { + 0, + }; + int32_t flags = 0; + dentry_t *dir_entry = NULL; + inode_table_t *table = NULL; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + local = frame->local; + GF_VALIDATE_OR_GOTO("trash", local, out); + + table = local->loc.inode->table; + + pthread_mutex_lock(&table->lock); + { + dir_entry = __dentry_search_arbit(local->loc.inode); + } + pthread_mutex_unlock(&table->lock); + + if (op_ret == -1) { + gf_log(this->name, GF_LOG_DEBUG, "fstat on the file failed: %s", + strerror(op_errno)); + + TRASH_STACK_UNWIND(truncate, frame, op_ret, op_errno, buf, NULL, xdata); + goto out; + } + + /* Only last hardlink will be moved to trash directory */ + if (buf->ia_nlink > 1) { + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); + goto out; + } + + /** + * If the file is too big or if it is extended truncate, + * just don't move it to trash directory. + */ + if (buf->ia_size > (priv->max_trash_file_size) || + buf->ia_size <= local->fop_offset) { + gf_log(this->name, GF_LOG_DEBUG, + "%s: file is too large to move to trash", local->loc.path); + + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); + goto out; + } + + /* Retrieves the name of file from path */ + local->loc.name = gf_strdup(strrchr(local->loc.path, '/')); + if (!local->loc.name) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + + /* Stores new path for source file */ + copy_trash_path(priv->newtrash_dir, (frame->root->pid < 0), local->newpath, + sizeof(local->newpath)); + strncat(local->newpath, local->loc.path, + sizeof(local->newpath) - strlen(local->newpath) - 1); + + /* append timestamp to file name so that we can avoid + name collisions inside trash */ + append_time_stamp(local->newpath, sizeof(local->newpath)); + if (strlen(local->newpath) > PATH_MAX) { + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, &local->loc, + local->fop_offset, xdata); + goto out; + } + + strncpy(loc_newname, local->loc.name, sizeof(loc_newname)); + loc_newname[sizeof(loc_newname) - 1] = 0; + append_time_stamp(loc_newname, sizeof(loc_newname)); + /* local->newloc represents old file(file inside trash), + where as local->loc represents truncated file. We need + to create new inode and fd for new file*/ + local->newloc.name = gf_strdup(loc_newname); + if (!local->newloc.name) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + local->newloc.path = gf_strdup(local->newpath); + if (!local->newloc.path) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; + } + local->newloc.inode = inode_new(local->loc.inode->table); + local->newfd = fd_create(local->newloc.inode, frame->root->pid); + + /* Creating valid parent and pargfids for both files */ + + if (dir_entry == NULL) { + ret = EINVAL; + goto out; + } + local->loc.parent = inode_ref(dir_entry->parent); + gf_uuid_copy(local->loc.pargfid, dir_entry->parent->gfid); + + local->newloc.parent = inode_ref(dir_entry->parent); + gf_uuid_copy(local->newloc.pargfid, dir_entry->parent->gfid); + + flags = O_CREAT | O_EXCL | O_WRONLY; + + TRASH_SET_PID(frame, local); + + STACK_WIND(frame, trash_truncate_create_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->create, &local->newloc, flags, + st_mode_from_ia(buf->ia_prot, local->loc.inode->ia_type), 0022, + local->newfd, xdata); - local = frame->local; - local->fsize = stbuf->ia_size; - - if (op_ret == -1) { - STACK_WIND (frame, trash_ftruncate_unlink_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, - &local->newloc); - return 0; - } +out: + return ret; +} - STACK_WIND (frame, trash_ftruncate_writev_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->writev, - local->newfd, vector, count, local->cur_offset, NULL); +/** + * Truncate can be explicitly called or implicitly by some other applications + * like text editors etc.. + */ +int32_t +trash_truncate(call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset, + dict_t *xdata) +{ + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + int32_t match = 0; + char *pathbuf = NULL; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + /* If trash is not active or not enabled through cli, then + * we bypass and wind back + */ + if (!priv->state) { + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, offset, xdata); + goto out; + } + + /* The files removed by gluster operations such as self-heal, + should moved to trash directory, but files by client should + not moved */ + if ((frame->root->pid < 0) && !priv->internal) { + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, offset, xdata); + goto out; + } + /* This will be more accurate */ + inode_path(loc->inode, NULL, &pathbuf); + + /* Checks whether file is in trash directory or eliminate path. + * In all such cases it does not move to trash directory, + * truncate will be performed + */ + match = check_whether_eliminate_path(priv->eliminate, pathbuf); + + if ((strncmp(pathbuf, priv->newtrash_dir, strlen(priv->newtrash_dir)) == + 0) || + (match)) { + if (match) { + gf_log(this->name, GF_LOG_DEBUG, + "%s: file not moved to trash as per option " + "'eliminate path'", + loc->path); + } + + /* Trying to truncate from the trash-dir. So do the + * actual truncate without moving to trash-dir. + */ + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, offset, xdata); + goto out; + } + + LOCK_INIT(&frame->lock); + + local = mem_get0(this->local_pool); + if (!local) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + TRASH_STACK_UNWIND(truncate, frame, -1, ENOMEM, NULL, NULL, xdata); + ret = ENOMEM; + goto out; + } + + strncpy(local->origpath, pathbuf, sizeof(local->origpath)); + local->origpath[sizeof(local->origpath) - 1] = 0; + + loc_copy(&local->loc, loc); + local->loc.path = pathbuf; + local->fop_offset = offset; + + frame->local = local; + + STACK_WIND(frame, trash_truncate_stat_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->stat, loc, xdata); - return 0; +out: + return ret; } +/** + * When we call truncate from terminal it comes to ftruncate of trash-xlator. + * Since truncate internally calls ftruncate and we receive fd of the file, + * other than that it also called by Rebalance operation + */ +int32_t +trash_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, + dict_t *xdata) +{ + trash_private_t *priv = NULL; + trash_local_t *local = NULL; /* file inside trash */ + char *pathbuf = NULL; /* path of file from fd */ + int32_t retval = 0; + int32_t match = 0; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + /* If trash is not active or not enabled through cli, then + * we bypass and wind back + */ + if (!priv->state) { + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata); + goto out; + } + + /* The files removed by gluster operations such as self-heal, + * should moved to trash directory, but files by client + * should not moved + */ + if ((frame->root->pid < 0) && !priv->internal) { + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata); + goto out; + } + /* This will be more accurate */ + retval = inode_path(fd->inode, NULL, &pathbuf); + + /* Checking the eliminate path */ + + /* Checks whether file is trash directory or eliminate path or + * invalid fd. In all such cases it does not move to trash directory, + * ftruncate will be performed + */ + match = check_whether_eliminate_path(priv->eliminate, pathbuf); + if ((strncmp(pathbuf, priv->newtrash_dir, strlen(priv->newtrash_dir)) == + 0) || + match || !retval) { + if (match) { + gf_log(this->name, GF_LOG_DEBUG, + "%s: file matches eliminate path, " + "not moved to trash", + pathbuf); + } + + /* Trying to ftruncate from the trash-dir. So do the + * actual ftruncate without moving to trash-dir + */ + STACK_WIND(frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata); + goto out; + } + + local = mem_get0(this->local_pool); + if (!local) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + TRASH_STACK_UNWIND(ftruncate, frame, -1, ENOMEM, NULL, NULL, xdata); + ret = -1; + goto out; + } + + strncpy(local->origpath, pathbuf, sizeof(local->origpath)); + local->origpath[sizeof(local->origpath) - 1] = 0; + + /* To convert fd to location */ + frame->local = local; + + local->loc.path = pathbuf; + local->loc.inode = inode_ref(fd->inode); + gf_uuid_copy(local->loc.gfid, local->loc.inode->gfid); + + local->fop_offset = offset; + + /* Else remains same to truncate code, so from here flow goes + * to truncate_stat + */ + STACK_WIND(frame, trash_truncate_stat_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fstat, fd, xdata); +out: + return ret; +} +/** + * The mkdir call is intercepted to avoid creation of + * trash directory in the mount by the user + */ int32_t -trash_ftruncate_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, fd_t *fd, - inode_t *inode, struct iatt *buf, - struct iatt *preparent, struct iatt *postparent) +trash_mkdir(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + mode_t umask, dict_t *xdata) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *dir_name = NULL; - char *tmp_path = NULL; - loc_t tmp_loc = {0,}; - - local = frame->local; - - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - dir_name = dirname (tmp_str); - - tmp_path = gf_strdup (dir_name); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; - - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, - tmp_path, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->mkdir, - &tmp_loc, 0755, NULL); - GF_FREE (tmp_str); - return 0; - } + int32_t op_ret = 0; + int32_t op_errno = 0; + trash_private_t *priv = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + if (!check_whether_op_permitted(priv, loc)) { + gf_log(this->name, GF_LOG_WARNING, + "mkdir issued on %s, which is not permitted", + priv->newtrash_dir); + op_errno = EPERM; + op_ret = -1; + + STACK_UNWIND_STRICT(mkdir, frame, op_ret, op_errno, NULL, NULL, NULL, + NULL, xdata); + } else { + STACK_WIND(frame, trash_common_mkdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, loc, mode, umask, xdata); + } - if (op_ret == -1) { - STACK_WIND (frame, trash_common_unwind_buf_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->ftruncate, - local->fd, local->fop_offset); - return 0; - } +out: + return 0; +} - STACK_WIND (frame, trash_ftruncate_readv_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->readv, local->fd, - (size_t)GF_BLOCK_READV_SIZE, local->cur_offset); +/** + * The rename call is intercepted to avoid renaming + * of trash directory in the mount by the user + */ +int +trash_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, + dict_t *xdata) +{ + int32_t op_ret = 0; + int32_t op_errno = 0; + trash_private_t *priv = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + if (!check_whether_op_permitted(priv, oldloc)) { + gf_log(this->name, GF_LOG_WARNING, + "rename issued on %s, which is not permitted", + priv->newtrash_dir); + op_errno = EPERM; + op_ret = -1; + + STACK_UNWIND_STRICT(rename, frame, op_ret, op_errno, NULL, NULL, NULL, + NULL, NULL, xdata); + } else { + STACK_WIND(frame, trash_common_rename_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, oldloc, newloc, xdata); + } - return 0; +out: + return 0; } - +/** + * The rmdir call is intercepted to avoid deletion of + * trash directory in the mount by the user + */ int32_t -trash_ftruncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *stbuf, struct iatt *preparent, - struct iatt *postparent) +trash_rmdir(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + dict_t *xdata) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *tmp_path = NULL; - char *tmp_dirname = NULL; - char *dir_name = NULL; - int32_t count = 0; - int32_t flags = 0; - int32_t loop_count = 0; - int i = 0; - loc_t tmp_loc = {0,}; - - local = frame->local; - if (!local) - goto out; + int32_t op_ret = 0; + int32_t op_errno = 0; + trash_private_t *priv = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + if (!check_whether_op_permitted(priv, loc)) { + gf_log(this->name, GF_LOG_WARNING, + "rmdir issued on %s, which is not permitted", + priv->newtrash_dir); + op_errno = EPERM; + op_ret = -1; + + STACK_UNWIND_STRICT(rmdir, frame, op_ret, op_errno, NULL, NULL, xdata); + } else { + STACK_WIND(frame, trash_common_rmdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rmdir, loc, flags, xdata); + } - loop_count = local->loop_count; +out: + return 0; +} - tmp_str = gf_strdup (local->newpath); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); +/** + * Volume set option is handled by the reconfigure function. + * Here we checks whether each option is set or not ,if it + * sets then corresponding modifciations will be made + */ +int +reconfigure(xlator_t *this, dict_t *options) +{ + uint64_t max_fsize = 0; + int ret = 0; + char *tmp = NULL; + char *tmp_str = NULL; + trash_private_t *priv = NULL; + char trash_dir[PATH_MAX] = { + 0, + }; + + priv = this->private; + + GF_VALIDATE_OR_GOTO("trash", priv, out); + + GF_OPTION_RECONF("trash-internal-op", priv->internal, options, bool, out); + GF_OPTION_RECONF("trash-dir", tmp, options, str, out); + + GF_OPTION_RECONF("trash", priv->state, options, bool, out); + + if (priv->state) { + ret = create_or_rename_trash_directory(this); + + if (tmp) + sprintf(trash_dir, "/%s/", tmp); + else + sprintf(trash_dir, "%s", priv->oldtrash_dir); + + if (strcmp(priv->newtrash_dir, trash_dir) != 0) { + /* When user set a new name for trash directory, trash + * xlator will perform a rename operation on old trash + * directory to the new one using a STACK_WIND from here. + * This option can be configured only when volume is in + * started state + */ + + GF_FREE(priv->newtrash_dir); + + priv->newtrash_dir = gf_strdup(trash_dir); + if (!priv->newtrash_dir) { + ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); goto out; - } - - if ((op_ret == -1) && (op_errno == ENOENT)) { - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - i++; - if (i > loop_count) - break; - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; - STACK_WIND_COOKIE (frame, trash_ftruncate_mkdir_cbk, - tmp_path, this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - + } + gf_log(this->name, GF_LOG_DEBUG, + "Renaming %s -> %s from reconfigure", priv->oldtrash_dir, + priv->newtrash_dir); + + if (!priv->newtrash_dir) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; goto out; + } + ret = rename_trash_directory(this); + } + + if (priv->internal) { + ret = create_internalop_directory(this); + } + } + tmp = NULL; + + GF_OPTION_RECONF("trash-max-filesize", max_fsize, options, size_uint64, + out); + if (max_fsize) { + priv->max_trash_file_size = max_fsize; + gf_log(this->name, GF_LOG_DEBUG, "%" GF_PRI_SIZET " max-size", + priv->max_trash_file_size); + } + GF_OPTION_RECONF("trash-eliminate-path", tmp, options, str, out); + if (!tmp) { + gf_log(this->name, GF_LOG_DEBUG, + "no option specified for 'eliminate', using NULL"); + } else { + if (priv->eliminate) + wipe_eliminate_path(&priv->eliminate); + + tmp_str = gf_strdup(tmp); + if (!tmp_str) { + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; + goto out; } + ret = store_eliminate_path(tmp_str, &priv->eliminate); + } - if (op_ret == 0) { - dir_name = dirname (tmp_str); - if (strcmp ((char*)cookie, dir_name) == 0) { - ia_prot_t prot = {0, }; - flags = O_CREAT|O_EXCL|O_WRONLY; - - //Call create again once directory structure is created. - STACK_WIND (frame, trash_ftruncate_create_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->create, - &local->newloc, flags, - st_mode_from_ia (prot, local->loc.inode->ia_type), - local->newfd, NULL); - goto out; - } - } +out: - LOCK (&frame->lock); - { - loop_count = ++local->loop_count; - } - UNLOCK (&frame->lock); - tmp_dirname = strchr (tmp_str, '/'); - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - - i++; - if ((i > loop_count) || (count > PATH_MAX)) - break; - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - tmp_path = memdup (local->newpath, count); - if (!tmp_path) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - tmp_loc.path = tmp_path; + return ret; +} - STACK_WIND_COOKIE (frame, trash_ftruncate_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); +/** + * Notify is used to create the trash directory with fixed gfid + * using STACK_WIND only when posix xlator is up + */ +int +notify(xlator_t *this, int event, void *data, ...) +{ + trash_private_t *priv = NULL; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO("trash", priv, out); + + /* Check whether posix is up not */ + if (event == GF_EVENT_CHILD_UP) { + if (!priv->state) { + gf_log(this->name, GF_LOG_DEBUG, "trash xlator is off"); + goto out; + } + + /* Here there is two possibilities ,if trash directory already + * exist ,then we need to perform a rename operation on the + * old one. Otherwise, we need to create the trash directory + * For both, we need to pass location variable, gfid of parent + * and a frame for calling STACK_WIND.The location variable + * requires name,path,gfid and inode + */ + if (!priv->oldtrash_dir) + ret = create_or_rename_trash_directory(this); + else if (strcmp(priv->newtrash_dir, priv->oldtrash_dir) != 0) + ret = rename_trash_directory(this); + if (ret) + goto out; + + if (priv->internal) + (void)create_internalop_directory(this); + } out: - GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ - if (tmp_str) - GF_FREE (tmp_str); - - return 0; + ret = default_notify(this, event, data); + if (ret) + gf_log(this->name, GF_LOG_INFO, "default notify event failed"); + return ret; } - int32_t -trash_ftruncate_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf) +mem_acct_init(xlator_t *this) { - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - - priv = this->private; - local = frame->local; - - if (op_ret == -1) { - gf_log (this->name, GF_LOG_DEBUG, - "%s: %s",local->newloc.path, strerror(op_errno)); - - TRASH_STACK_UNWIND (ftruncate, frame, -1, op_errno, buf, NULL); - return 0; - } - if ((buf->ia_size == 0) || (buf->ia_size > priv->max_trash_file_size)) - { - STACK_WIND (frame, trash_common_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->ftruncate, - local->fd, local->fop_offset); - return 0; - } - + int ret = -1; - STACK_WIND (frame, trash_ftruncate_create_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->create, &local->newloc, - ( O_CREAT | O_EXCL | O_WRONLY ), - st_mode_from_ia (buf->ia_prot, local->loc.inode->ia_type), - local->newfd, NULL); + GF_VALIDATE_OR_GOTO("trash", this, out); - return 0; + ret = xlator_mem_acct_init(this, gf_trash_mt_end + 1); + if (ret != 0) { + gf_log(this->name, GF_LOG_ERROR, + "Memory accounting init" + "failed"); + return ret; + } +out: + return ret; } +/** + * trash_init + */ int32_t -trash_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset) +init(xlator_t *this) { - trash_elim_pattern_t *trav = NULL; - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - dentry_t *dir_entry = NULL; - struct tm *tm = NULL; - char *pathbuf = NULL; - inode_t *newinode = NULL; - time_t utime = 0; - char timestr[256]; - int32_t retval = 0; - int32_t match = 0; - - priv = this->private; - - dir_entry = __dentry_search_arbit (fd->inode); - retval = inode_path (fd->inode, NULL, &pathbuf); - - if (priv->eliminate) { - trav = priv->eliminate; - while (trav) { - if (fnmatch(trav->pattern, dir_entry->name, 0) == 0) { - match++; - break; - } - trav = trav->next; - } - } - - if ((strncmp (pathbuf, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || - (offset >= priv->max_trash_file_size) || - (!retval) || - match) { - STACK_WIND (frame, trash_common_unwind_buf_cbk, - FIRST_CHILD(this), - FIRST_CHILD(this)->fops->ftruncate, - fd, offset); - return 0; - } - - local = GF_CALLOC (1, sizeof (trash_local_t), - gf_trash_mt_trash_local_t); - if (!local) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - TRASH_STACK_UNWIND (ftruncate, frame, -1, ENOMEM, NULL, NULL); - return 0; - } - - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y-%m-%d-%H%M%S", tm); - - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, pathbuf); - strcat (local->newpath, timestr); - - local->fd = fd_ref (fd); - newinode = inode_new (fd->inode->table); - local->newfd = fd_create (newinode, frame->root->pid); - frame->local=local; - - local->newloc.inode = newinode; - local->newloc.path = local->newpath; - - local->loc.inode = inode_ref (fd->inode); - local->loc.path = pathbuf; - - local->fop_offset = offset; - local->cur_offset = offset; - - STACK_WIND (frame, trash_ftruncate_fstat_cbk, this->children->xlator, - this->children->xlator->fops->fstat, fd); + trash_private_t *priv = NULL; + int ret = -1; + char *tmp = NULL; + char *tmp_str = NULL; + char trash_dir[PATH_MAX] = { + 0, + }; + uint64_t max_trash_file_size64 = 0; + data_t *data = NULL; + + GF_VALIDATE_OR_GOTO("trash", this, out); + + if (!this->children || this->children->next) { + gf_log(this->name, GF_LOG_ERROR, + "not configured with exactly one child. exiting"); + ret = -1; + goto out; + } + + if (!this->parents) { + gf_log(this->name, GF_LOG_WARNING, "dangling volume. check volfile"); + } + + priv = GF_CALLOC(1, sizeof(*priv), gf_trash_mt_trash_private_t); + if (!priv) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + + /* Trash priv data members are initialized through the following + * set of statements + */ + GF_OPTION_INIT("trash", priv->state, bool, out); + + GF_OPTION_INIT("trash-dir", tmp, str, out); + + /* We store trash dir value as path for easier manipulation*/ + if (!tmp) { + gf_log(this->name, GF_LOG_INFO, + "no option specified for 'trash-dir', " + "using \"/.trashcan/\""); + priv->newtrash_dir = gf_strdup("/.trashcan/"); + if (!priv->newtrash_dir) { + ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + } else { + sprintf(trash_dir, "/%s/", tmp); + priv->newtrash_dir = gf_strdup(trash_dir); + if (!priv->newtrash_dir) { + ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + } + tmp = NULL; + + GF_OPTION_INIT("trash-eliminate-path", tmp, str, out); + if (!tmp) { + gf_log(this->name, GF_LOG_INFO, + "no option specified for 'eliminate', using NULL"); + } else { + tmp_str = gf_strdup(tmp); + if (!tmp_str) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + ret = store_eliminate_path(tmp_str, &priv->eliminate); + } + tmp = NULL; + + GF_OPTION_INIT("trash-max-filesize", max_trash_file_size64, size_uint64, + out); + if (!max_trash_file_size64) { + gf_log(this->name, GF_LOG_ERROR, + "no option specified for 'max-trashable-file-size', " + "using default = %lld MB", + GF_DEFAULT_MAX_FILE_SIZE / GF_UNIT_MB); + priv->max_trash_file_size = GF_DEFAULT_MAX_FILE_SIZE; + } else { + priv->max_trash_file_size = max_trash_file_size64; + gf_log(this->name, GF_LOG_DEBUG, "%" GF_PRI_SIZET " max-size", + priv->max_trash_file_size); + } + + GF_OPTION_INIT("trash-internal-op", priv->internal, bool, out); + + this->local_pool = mem_pool_new(trash_local_t, 64); + if (!this->local_pool) { + gf_log(this->name, GF_LOG_ERROR, + "failed to create local_t's memory pool"); + ret = ENOMEM; + goto out; + } + + /* For creating directories inside trash with proper permissions, + * we need to perform stat on that directories, for this we use + * brick path + */ + data = dict_get(this->options, "brick-path"); + if (!data) { + gf_log(this->name, GF_LOG_ERROR, + "no option specified for 'brick-path'"); + ret = ENOMEM; + goto out; + } + priv->brick_path = gf_strdup(data->data); + if (!priv->brick_path) { + ret = ENOMEM; + gf_log(this->name, GF_LOG_DEBUG, "out of memory"); + goto out; + } + + priv->trash_itable = inode_table_new(0, this); + gf_log(this->name, GF_LOG_DEBUG, "brick path is%s", priv->brick_path); + + this->private = (void *)priv; + ret = 0; - return 0; +out: + if (tmp_str) + GF_FREE(tmp_str); + if (ret) { + if (priv) { + if (priv->newtrash_dir) + GF_FREE(priv->newtrash_dir); + if (priv->oldtrash_dir) + GF_FREE(priv->oldtrash_dir); + if (priv->brick_path) + GF_FREE(priv->brick_path); + if (priv->eliminate) + wipe_eliminate_path(&priv->eliminate); + GF_FREE(priv); + } + mem_pool_destroy(this->local_pool); + this->local_pool = NULL; + } + return ret; } /** - * trash_init - + * trash_fini */ -int32_t -init (xlator_t *this) +void +fini(xlator_t *this) { - data_t *data = NULL; - trash_private_t *_priv = NULL; - trash_elim_pattern_t *trav = NULL; - char *tmp_str = NULL; - char *strtokptr = NULL; - char *component = NULL; - char trash_dir[PATH_MAX] = {0,}; - uint64_t max_trash_file_size64 = 0; - - /* Create .trashcan directory in init */ - if (!this->children || this->children->next) { - gf_log (this->name, GF_LOG_ERROR, - "not configured with exactly one child. exiting"); - return -1; - } + trash_private_t *priv = NULL; + inode_table_t *inode_table = NULL; - if (!this->parents) { - gf_log (this->name, GF_LOG_WARNING, - "dangling volume. check volfile "); + GF_VALIDATE_OR_GOTO("trash", this, out); + priv = this->private; + if (priv) { + inode_table = priv->trash_itable; + if (priv->newtrash_dir) { + GF_FREE(priv->newtrash_dir); + priv->newtrash_dir = NULL; } - - _priv = GF_CALLOC (1, sizeof (*_priv), gf_trash_mt_trash_private_t); - if (!_priv) { - gf_log (this->name, GF_LOG_ERROR, "out of memory"); - return -1; + if (priv->oldtrash_dir) { + GF_FREE(priv->oldtrash_dir); + priv->oldtrash_dir = NULL; } - - data = dict_get (this->options, "trash-dir"); - if (!data) { - gf_log (this->name, GF_LOG_INFO, - "no option specified for 'trash-dir', " - "using \"/.trashcan/\""); - _priv->trash_dir = gf_strdup ("/.trashcan"); - } else { - /* Need a path with '/' as the first char, if not - given, append it */ - if (data->data[0] == '/') { - _priv->trash_dir = gf_strdup (data->data); - } else { - /* TODO: Make sure there is no ".." in the path */ - strcpy (trash_dir, "/"); - strcat (trash_dir, data->data); - _priv->trash_dir = gf_strdup (trash_dir); - } + if (priv->brick_path) { + GF_FREE(priv->brick_path); + priv->brick_path = NULL; } - - data = dict_get (this->options, "eliminate-pattern"); - if (!data) { - gf_log (this->name, GF_LOG_TRACE, - "no option specified for 'eliminate', using NULL"); - } else { - tmp_str = gf_strdup (data->data); - if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - } - - /* Match Filename to option specified in eliminate. */ - component = strtok_r (tmp_str, "|", &strtokptr); - while (component) { - trav = GF_CALLOC (1, sizeof (*trav), - gf_trash_mt_trash_elim_pattern_t); - if (!trav) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); - break; - } - trav->pattern = component; - trav->next = _priv->eliminate; - _priv->eliminate = trav; - - component = strtok_r (NULL, "|", &strtokptr); - } + if (priv->eliminate) { + wipe_eliminate_path(&priv->eliminate); + priv->eliminate = NULL; } - - /* TODO: do gf_string2sizet () */ - data = dict_get (this->options, "max-trashable-file-size"); - if (!data) { - gf_log (this->name, GF_LOG_DEBUG, - "no option specified for 'max-trashable-file-size', " - "using default = %lld MB", - GF_DEFAULT_MAX_FILE_SIZE / GF_UNIT_MB); - _priv->max_trash_file_size = GF_DEFAULT_MAX_FILE_SIZE; - } else { - (void)gf_string2bytesize (data->data, - &max_trash_file_size64); - if( max_trash_file_size64 > GF_ALLOWED_MAX_FILE_SIZE ) { - gf_log (this->name, GF_LOG_DEBUG, - "Size specified for max-size(in MB) is too " - "large so using 1GB as max-size (NOT IDEAL)"); - _priv->max_trash_file_size = GF_ALLOWED_MAX_FILE_SIZE; - } else - _priv->max_trash_file_size = max_trash_file_size64; - gf_log (this->name, GF_LOG_DEBUG, "%"GF_PRI_SIZET" max-size", - _priv->max_trash_file_size); + if (inode_table) { + inode_table_destroy(inode_table); + priv->trash_itable = NULL; } + GF_FREE(priv); + } - this->private = (void *)_priv; - return 0; -} - -void -fini (xlator_t *this) -{ - trash_private_t *priv = NULL; - - priv = this->private; - if (priv) - GF_FREE (priv); - - return; + if (this->local_pool) { + mem_pool_destroy(this->local_pool); + this->local_pool = NULL; + } + this->private = NULL; +out: + return; } struct xlator_fops fops = { - .unlink = trash_unlink, - .rename = trash_rename, - .truncate = trash_truncate, - .ftruncate = trash_ftruncate, + .unlink = trash_unlink, + .truncate = trash_truncate, + .ftruncate = trash_ftruncate, + .rmdir = trash_rmdir, + .mkdir = trash_mkdir, + .rename = trash_rename, }; -struct xlator_cbks cbks = { -}; +struct xlator_cbks cbks = {}; struct volume_options options[] = { - { .key = { "trash-directory" }, - .type = GF_OPTION_TYPE_PATH, - }, - { .key = { "eliminate-pattern" }, - .type = GF_OPTION_TYPE_STR, - }, - { .key = { "max-trashable-file-size" }, - .type = GF_OPTION_TYPE_SIZET, - }, - { .key = {NULL} }, + { + .key = {"trash"}, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "Enable/disable trash translator", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {"backup"}, + }, + { + .key = {"trash-dir"}, + .type = GF_OPTION_TYPE_STR, + .default_value = ".trashcan", + .description = "Directory for trash files", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {"backup"}, + }, + { + .key = {"trash-eliminate-path"}, + .type = GF_OPTION_TYPE_STR, + .description = "Eliminate paths to be excluded " + "from trashing", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {"backup"}, + }, + { + .key = {"trash-max-filesize"}, + .type = GF_OPTION_TYPE_SIZET, + .default_value = "5MB", + .description = "Maximum size of file that can be " + "moved to trash", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {"backup"}, + }, + { + .key = {"trash-internal-op"}, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "Enable/disable trash translator for " + "internal operations", + .op_version = {GD_OP_VERSION_3_7_0}, + .flags = OPT_FLAG_SETTABLE | OPT_FLAG_DOC, + .tags = {"backup"}, + }, + {.key = {"brick-path"}, + .type = GF_OPTION_TYPE_PATH, + .default_value = "{{ brick.path }}"}, + {.key = {NULL}}, +}; + +xlator_api_t xlator_api = { + .init = init, + .fini = fini, + .notify = notify, + .reconfigure = reconfigure, + .mem_acct_init = mem_acct_init, + .op_version = {1}, /* Present from the initial version */ + .fops = &fops, + .cbks = &cbks, + .options = options, + .identifier = "trash", + .category = GF_TECH_PREVIEW, }; diff --git a/xlators/features/trash/src/trash.h b/xlators/features/trash/src/trash.h index d385ee34657..6671617c2c6 100644 --- a/xlators/features/trash/src/trash.h +++ b/xlators/features/trash/src/trash.h @@ -1,89 +1,97 @@ /* - Copyright (c) 2006-2011 Gluster, Inc. <http://www.gluster.com> - This file is part of GlusterFS. + Copyright (c) 2006-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 __TRASH_H__ #define __TRASH_H__ -#ifndef _CONFIG_H -#define _CONFIG_H -#include "config.h" -#endif - -#include "glusterfs.h" -#include "logging.h" -#include "dict.h" -#include "xlator.h" -#include "defaults.h" +#include <glusterfs/glusterfs.h> +#include <glusterfs/logging.h> +#include <glusterfs/dict.h> +#include <glusterfs/xlator.h> +#include <glusterfs/defaults.h> #include "inode.c" #include "fnmatch.h" #include <libgen.h> #ifndef GF_BLOCK_READV_SIZE -#define GF_BLOCK_READV_SIZE (128 * GF_UNIT_KB) +#define GF_BLOCK_READV_SIZE (128 * GF_UNIT_KB) #endif #ifndef GF_DEFAULT_MAX_FILE_SIZE #define GF_DEFAULT_MAX_FILE_SIZE (200 * GF_UNIT_MB) #endif -#ifndef GF_ALLOWED_MAX_FILE_SIZE -#define GF_ALLOWED_MAX_FILE_SIZE (1 * GF_UNIT_GB) -#endif - - struct trash_struct { - fd_t *fd; /* for the fd of existing file */ - fd_t *newfd; /* for the newly created file */ - loc_t loc; /* to store the location of the existing file */ - loc_t newloc; /* to store the location for the new file */ - size_t fsize; /* for keeping the size of existing file */ - off_t cur_offset; /* current offset for read and write ops */ - off_t fop_offset; - char origpath[PATH_MAX]; - char newpath[PATH_MAX]; - int32_t loop_count; - struct iatt preparent; - struct iatt postparent; + fd_t *fd; /* for the fd of existing file */ + fd_t *newfd; /* for the newly created file */ + loc_t loc; /* to store the location of the existing file */ + loc_t newloc; /* to store the location for the new file */ + size_t fsize; /* for keeping the size of existing file */ + off_t cur_offset; /* current offset for read and write ops */ + off_t fop_offset; /* original offset received with the fop */ + pid_t pid; + char origpath[PATH_MAX]; + char newpath[PATH_MAX]; + int32_t loop_count; + gf_boolean_t is_set_pid; + struct iatt preparent; + struct iatt postparent; + gf_boolean_t ctr_link_count_req; }; typedef struct trash_struct trash_local_t; -struct _trash_elim_pattern; -typedef struct _trash_elim_pattern { - struct _trash_elim_pattern *next; - char *pattern; -} trash_elim_pattern_t; +struct _trash_elim_path { + struct _trash_elim_path *next; + char *path; +}; +typedef struct _trash_elim_path trash_elim_path; struct trash_priv { - char *trash_dir; - trash_elim_pattern_t *eliminate; - size_t max_trash_file_size; + char *oldtrash_dir; + char *newtrash_dir; + char *brick_path; + trash_elim_path *eliminate; + size_t max_trash_file_size; + gf_boolean_t state; + gf_boolean_t internal; + inode_t *trash_inode; + inode_table_t *trash_itable; }; typedef struct trash_priv trash_private_t; -#define TRASH_STACK_UNWIND(op, frame, params ...) do { \ - trash_local_t *__local = NULL; \ - __local = frame->local; \ - frame->local = NULL; \ - STACK_UNWIND_STRICT (op, frame, params); \ - trash_local_wipe (__local); \ - } while (0) - +#define TRASH_SET_PID(frame, local) \ + do { \ + GF_ASSERT(!local->is_set_pid); \ + if (!local->is_set_pid) { \ + local->pid = frame->root->pid; \ + frame->root->pid = GF_SERVER_PID_TRASH; \ + local->is_set_pid = _gf_true; \ + } \ + } while (0) + +#define TRASH_UNSET_PID(frame, local) \ + do { \ + GF_ASSERT(local->is_set_pid); \ + if (local->is_set_pid) { \ + frame->root->pid = local->pid; \ + local->is_set_pid = _gf_false; \ + } \ + } while (0) + +#define TRASH_STACK_UNWIND(op, frame, params...) \ + do { \ + trash_local_t *__local = NULL; \ + __local = frame->local; \ + frame->local = NULL; \ + STACK_UNWIND_STRICT(op, frame, params); \ + trash_local_wipe(__local); \ + } while (0) #endif /* __TRASH_H__ */ |
