diff options
Diffstat (limited to 'xlators/features/trash/src/trash.c')
| -rw-r--r-- | xlators/features/trash/src/trash.c | 1973 |
1 files changed, 1457 insertions, 516 deletions
diff --git a/xlators/features/trash/src/trash.c b/xlators/features/trash/src/trash.c index b2c4a9ab3..addeb66a0 100644 --- a/xlators/features/trash/src/trash.c +++ b/xlators/features/trash/src/trash.c @@ -1,512 +1,1392 @@ /* - Copyright (c) 2006-2009 Z RESEARCH, Inc. <http://www.zresearch.com> - This file is part of GlusterFS. - - GlusterFS is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - GlusterFS is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. -*/ + 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. +*/ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif +#include "trash.h" +#include "trash-mem-types.h" -#include "glusterfs.h" -#include "logging.h" -#include "dict.h" -#include "xlator.h" -#include "defaults.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); -#include <libgen.h> +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); -/* TODO: currently it can work only above posix, no other translators - * between them. Not a good thing. Try making more reliable methods. - */ +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); -struct trash_struct { - inode_t *inode; - loc_t loc1; - loc_t loc2; - char origpath[ZR_PATH_MAX]; - char newpath[ZR_PATH_MAX]; - char oldpath[ZR_PATH_MAX]; // used only in case of rename -}; -typedef struct trash_struct trash_local_t; +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); -struct trash_priv { - char trash_dir[ZR_PATH_MAX]; -}; -typedef struct trash_priv trash_private_t; +void +trash_local_wipe (trash_local_t *local) +{ + 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); + + mem_put (local); +out: + return; +} + +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) +{ + TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, preparent, postparent); + return 0; +} int32_t -trash_unlink_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf); +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) +{ + 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); + GF_FREE (tmp_str); + + return 0; +} + int32_t -trash_rename_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf); +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_common_unwind_cbk - - */ int32_t -trash_common_unwind_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno) +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_local_t *local = frame->local; + 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); - if (local->loc1.path) - loc_wipe (&local->loc1); - - if (local->loc2.path) - loc_wipe (&local->loc2); + return 0; + } - STACK_UNWIND (frame, op_ret, op_errno); - 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); + + STACK_WIND (frame, trash_common_unwind_cbk, + this->children->xlator, + this->children->xlator->fops->unlink, &local->loc); + + return 0; + } + + 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; } -/** - * trash_common_unwind_buf_cbk - - */ + + +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) +{ + TRASH_STACK_UNWIND (truncate, frame, op_ret, op_errno, prebuf, postbuf); + return 0; +} + +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) +{ + TRASH_STACK_UNWIND (rename, frame, op_ret, op_errno, stbuf, preoldparent, + postoldparent, prenewparent, postnewparent); + return 0; +} + + 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 stat *buf) +trash_unlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *buf) { - trash_local_t *local = frame->local; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + loc_t new_loc = {0,}; + + priv = this->private; + local = frame->local; - if (local->loc1.path) - loc_wipe (&local->loc1); - - if (local->loc2.path) - loc_wipe (&local->loc2); + 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 0; + +fail: + TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, buf, + NULL); + + return 0; - STACK_UNWIND (frame, op_ret, op_errno, buf); - return 0; } int32_t -trash_mkdir_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - inode_t *inode, - struct stat *stbuf) +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) { - trash_local_t *local = frame->local; - char *tmp_str = strdup (local->newpath); - int32_t count = 0; - char *tmp_path = NULL; - char *tmp_dirname = 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; - tmp_path = CALLOC (1, count + 1); - ERR_ABORT (tmp_path); - memcpy (tmp_path, local->newpath, count); - loc_t tmp_loc = { - .inode = NULL, - .path = tmp_path, - }; - - /* TODO:create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_mkdir_cbk, - tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - free (cookie); - free (tmp_str); - return 0; - } - char *dir_name = dirname (tmp_str); - if (strcmp((char*)cookie, dir_name) == 0) { - loc_t new_loc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_unlink_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc2, - &new_loc); - - } - free (cookie); /* strdup (dir_name) was sent here :) */ - free (tmp_str); - return 0; + 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); + } + + STACK_WIND (frame, trash_common_rename_cbk, + this->children->xlator, + this->children->xlator->fops->rename, &local->loc, + &local->newloc); + + return 0; +} + + +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_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; + } + + 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; + + 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; + + /* 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); + } + + goto out; + } + + dir_name = dirname (tmp_str); + if (strcmp ((char*)cookie, dir_name) == 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); + } + +out: + GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ + GF_FREE (tmp_str); + + return 0; } -/** - * trash_unlink_rename_cbk - - */ int32_t -trash_unlink_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf) +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_local_t *local = frame->local; - if (op_ret == -1 && op_errno == ENOENT) { - /* check for the errno, if its ENOENT create directory and call - * rename later - */ - char *tmp_str = strdup (local->newpath); - char *dir_name = dirname (tmp_str); - loc_t tmp_loc = { - .inode = NULL, - .path = dir_name, - }; - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_mkdir_cbk, - strdup (dir_name), - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - free (tmp_str); - } else if (op_ret == -1 && op_errno == ENOTDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists, cannot keep the copy, deleting"); - STACK_WIND (frame, - trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, - &local->loc2); - } else if (op_ret == -1 && op_errno == EISDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists as a directory, cannot keep the copy, " - "deleting"); - STACK_WIND (frame, - trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, - &local->loc2); - } else { - /* */ - STACK_UNWIND (frame, 0, op_errno); - } - - return 0; + 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_unlink - - */ int32_t -trash_unlink (call_frame_t *frame, - xlator_t *this, - loc_t *loc) +trash_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, + loc_t *newloc) { - trash_private_t *priv = this->private; - trash_local_t *local = NULL; - time_t utime = 0; - struct tm *tm = NULL; - char timestr[256]; - - if (strncmp (loc->path, priv->trash_dir, - strlen(priv->trash_dir)) == 0) { - /* Trying to rename from the trash can dir, do the - actual unlink */ - STACK_WIND (frame, - trash_common_unwind_cbk, - this->children->xlator, - this->children->xlator->fops->unlink, - loc); - } else { - local = CALLOC (1, sizeof (trash_local_t)); - if (!local) { - STACK_UNWIND (frame, -1, ENOMEM); - return 0; - } - frame->local = local; - - loc_copy (&local->loc2, loc); - - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, loc->path); - - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y%m%d%H%M%S", tm); - strcat (local->newpath, timestr); - - { - loc_t new_loc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_unlink_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - loc, - &new_loc); - } - } - return 0; + trash_elim_pattern_t *trav = NULL; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + char timestr[64] = {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; + } + } + + 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); + + return 0; + } + + local = mem_get0 (this->local_pool); + 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; + } + + 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? */ + gf_time_ftm (timestr, sizeof timestr, time (NULL), + gf_timefmt_F_HMS); + strcat (local->newpath, timestr); + } + + /* 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); + + return 0; } -/* */ 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 stat *stbuf) +trash_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) { - trash_local_t *local = frame->local; - char *tmp_str = strdup (local->newpath); - - if (op_ret == -1 && op_errno == ENOENT) { - int32_t count = 0; - char *tmp_path = NULL; - char *tmp_dirname = strchr (tmp_str, '/'); - - while (tmp_dirname) { - count = tmp_dirname - tmp_str; - if (count == 0) - count = 1; - tmp_path = CALLOC (1, count + 2); - ERR_ABORT (tmp_path); - memcpy (tmp_path, local->newpath, count); - loc_t tmp_loc = { - .inode = NULL, - .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, - 0777); - tmp_dirname = strchr (tmp_str + count + 1, '/'); - } - free (cookie); - free (tmp_str); - return 0; - } - char *dir_name = dirname (tmp_str); - if (strcmp((char*)cookie, dir_name) == 0) { - loc_t new_loc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc2, - &new_loc); - - } - free (cookie); /* strdup (dir_name) was sent here :) */ - free (tmp_str); - return 0; + trash_elim_pattern_t *trav = NULL; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + char timestr[64] = {0,}; + int32_t match = 0; + + priv = this->private; + + if (priv->eliminate) { + trav = priv->eliminate; + while (trav) { + if (fnmatch(trav->pattern, loc->name, 0) == 0) { + match++; + break; + } + trav = trav->next; + } + } + + 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; + } + + 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); + 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? */ + gf_time_fmt (timestr, sizeof timestr, time (NULL), + gf_timefmt_F_HMS); + strcat (local->newpath, timestr); + } + + LOCK_INIT (&frame->lock); + + STACK_WIND (frame, trash_unlink_stat_cbk, + this->children->xlator, + this->children->xlator->fops->stat, loc); + + return 0; } +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) +{ + /* 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)); + } + + STACK_WIND (frame, trash_common_unwind_buf_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, + &local->loc, local->fop_offset); + + return 0; +} -/** - * trash_unlink_rename_cbk - - */ int32_t -trash_rename_rename_cbk (call_frame_t *frame, - void *cookie, - xlator_t *this, - int32_t op_ret, - int32_t op_errno, - struct stat *buf) +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_local_t *local = frame->local; - if (op_ret == -1 && op_errno == ENOENT) { - /* check for the errno, if its ENOENT create directory and call - * rename later - */ - char *tmp_str = strdup (local->newpath); - char *dir_name = dirname (tmp_str); - loc_t tmp_loc = { - .inode = NULL, - .path = dir_name, - }; - /* TODO: create the directory with proper permissions */ - STACK_WIND_COOKIE (frame, - trash_rename_mkdir_cbk, - strdup (dir_name), - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, - 0777); - free (tmp_str); - return 0; - } else if (op_ret == -1 && op_errno == ENOTDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists, cannot keep the dest entry %s, " - "renaming", - local->loc2.path); - } else if (op_ret == -1 && op_errno == EISDIR) { - gf_log (this->name, GF_LOG_WARNING, - "Target exists as a directory, cannot keep the " - "copy %s, renaming", - local->loc2.path); - } - loc_t new_loc = { - .inode = NULL, - .parent = local->loc2.parent, - .path = local->loc2.path, - }; - STACK_WIND (frame, - trash_common_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc1, - &new_loc); - - return 0; + trash_local_t *local = NULL; + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_DEBUG, + "readv 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; + } + + 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); + +out: + return 0; + } -/** - * trash_rename_lookup_cbk - - */ 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 stat *buf, - dict_t *xattr) +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_local_t *local = frame->local; - - if (op_ret == -1) { - STACK_WIND (frame, - trash_common_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc1, - &local->loc2); - return 0; - } - - loc_t oldloc = { - .parent = local->loc2.parent, - .inode = inode, - .path = local->loc2.path, - }; - loc_t newloc = { - .inode = NULL, - .path = local->newpath - }; - STACK_WIND (frame, - trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &oldloc, - &newloc); - - return 0; + 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, 0); + 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); + +out: + return 0; +} + + + +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_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)); + + STACK_WIND (frame, trash_truncate_unlink_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, + &local->newloc); + goto out; + } + + local->cur_offset = local->fop_offset; + + 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); + +out: + return 0; +} + + +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_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; + } + + flags = O_RDONLY; + + 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: + return 0; +} + +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_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; + } + + 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; + } + } + + 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; + + STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, tmp_path, + this->children->xlator, + this->children->xlator->fops->mkdir, + &tmp_loc, 0755, NULL); + +out: + GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ + GF_FREE (tmp_str); + + return 0; +} + + +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_private_t *priv = NULL; + trash_local_t *local = NULL; + char timestr[64] = {0,}; + char loc_newname[PATH_MAX] = {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); + + { + gf_time_fmt (timestr, sizeof timestr, time (NULL), + gf_timefmt_F_HMS); + 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); + + 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; +} + +int32_t +trash_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, + off_t offset) +{ + trash_elim_pattern_t *trav = NULL; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + int32_t match = 0; + + priv = this->private; + if (priv->eliminate) { + trav = priv->eliminate; + while (trav) { + if (fnmatch(trav->pattern, loc->name, 0) == 0) { + match++; + break; + } + trav = trav->next; + } + } + + 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 = 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); + return 0; + } + + loc_copy (&local->loc, loc); + + local->fop_offset = offset; + + frame->local = local; + + STACK_WIND (frame, trash_truncate_stat_cbk, + this->children->xlator, + this->children->xlator->fops->stat, loc); + +out: + return 0; +} + +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_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)); + + } + + STACK_WIND (frame, trash_common_unwind_buf_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->ftruncate, + local->fd, local->fop_offset); + + return 0; +} + +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_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, 0); + return 0; + } + + STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, local->fd, + local->fop_offset); + + return 0; +} + + +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_local_t *local = NULL; + + 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; + } + + STACK_WIND (frame, trash_ftruncate_writev_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->writev, + local->newfd, vector, count, local->cur_offset, 0, NULL); + + return 0; +} + + +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_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; + } + + 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; + } + + 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, 0); + + return 0; } -/** - * trash_rename - - */ int32_t -trash_rename (call_frame_t *frame, - xlator_t *this, - loc_t *oldloc, - loc_t *newloc) +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_private_t *priv = this->private; - trash_local_t *local = NULL; - time_t utime = 0; - struct tm *tm = NULL; - char timestr[256]; - - if (strncmp (oldloc->path, priv->trash_dir, - strlen(priv->trash_dir)) == 0) { - /* Trying to rename from the trash can dir, - do the actual rename */ - STACK_WIND (frame, - trash_common_unwind_buf_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - oldloc, - newloc); - } else { - /* Trying to rename a regular file from GlusterFS */ - local = CALLOC (1, sizeof (trash_local_t)); - if (!local) { - STACK_UNWIND (frame, -1, ENOMEM, NULL); - return 0; - } - frame->local = local; - loc_copy (&local->loc1, oldloc); - loc_copy (&local->loc2, newloc); - - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, newloc->path); - - utime = time (NULL); - tm = localtime (&utime); - strftime (timestr, 256, ".%Y%m%d%H%M%S", tm); - strcat (local->newpath, timestr); - - /* 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); - } - return 0; + 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_ftruncate_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) { + 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; + } + } + + 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; + + STACK_WIND_COOKIE (frame, trash_ftruncate_mkdir_cbk, tmp_path, + this->children->xlator, + this->children->xlator->fops->mkdir, + &tmp_loc, 0755, NULL); + +out: + GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ + GF_FREE (tmp_str); + + return 0; +} + + +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) +{ + 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; + } + + + 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); + + return 0; +} + +int32_t +trash_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset) +{ + trash_elim_pattern_t *trav = NULL; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + dentry_t *dir_entry = NULL; + char *pathbuf = NULL; + inode_t *newinode = NULL; + char timestr[64]; + 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 = 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); + return 0; + } + + gf_time_fmt (timestr, sizeof timestr, time (NULL), gf_timefmt_F_HMS); + 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); + + return 0; } /** @@ -515,82 +1395,143 @@ trash_rename (call_frame_t *frame, int32_t init (xlator_t *this) { - data_t *trash_dir = NULL; - xlator_list_t *trav = NULL; - trash_private_t *_priv = NULL; - - /* 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; - } - - if (!this->parents) { - gf_log (this->name, GF_LOG_WARNING, - "dangling volume. check volfile "); - } - - trav = this->children; - while (trav->xlator->children) - trav = trav->xlator->children; - - if (strncmp ("storage/", trav->xlator->type, 8)) - { - gf_log (this->name, GF_LOG_ERROR, - "'trash' translator not loaded over storage " - "translator, not a supported setup"); - return -1; - } - - _priv = CALLOC (1, sizeof (*_priv)); - ERR_ABORT (_priv); - - trash_dir = dict_get (this->options, "trash-dir"); - if (!trash_dir) { - gf_log (this->name, GF_LOG_WARNING, - "no option specified for 'trash-dir', " - "using \"/.trashcan/\""); - strcpy (_priv->trash_dir, "/.trashcan"); - } else { - /* Need a path with '/' as the first char, if not - given, append it */ - if (trash_dir->data[0] == '/') { - strcpy (_priv->trash_dir, trash_dir->data); - } else { - strcpy (_priv->trash_dir, "/"); - strcat (_priv->trash_dir, trash_dir->data); - } - } - - this->private = (void *)_priv; - return 0; + 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; + } + + 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"); + return -1; + } + + 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); + } + } + + 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); + } + } + + /* 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); + } + + 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"); + return -1; + } + + + this->private = (void *)_priv; + return 0; } void fini (xlator_t *this) { - trash_private_t *priv = this->private; - FREE (priv); - return; -} + trash_private_t *priv = NULL; + priv = this->private; + GF_FREE (priv); -struct xlator_fops fops = { - .unlink = trash_unlink, - .rename = trash_rename, -}; - -struct xlator_mops mops = { + return; +} +struct xlator_fops fops = { + .unlink = trash_unlink, + .rename = trash_rename, + .truncate = trash_truncate, + .ftruncate = trash_ftruncate, }; struct xlator_cbks cbks = { }; struct volume_options options[] = { - { .key = { "trash-dir" }, - .type = GF_OPTION_TYPE_PATH - }, - { .key = {NULL} }, + { .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} }, }; |
