summaryrefslogtreecommitdiffstats
path: root/xlators/features/trash/src/trash.c
diff options
context:
space:
mode:
Diffstat (limited to 'xlators/features/trash/src/trash.c')
-rw-r--r--xlators/features/trash/src/trash.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/xlators/features/trash/src/trash.c b/xlators/features/trash/src/trash.c
new file mode 100644
index 00000000000..c8e7357ee08
--- /dev/null
+++ b/xlators/features/trash/src/trash.c
@@ -0,0 +1,596 @@
+/*
+ Copyright (c) 2006, 2007, 2008 Z RESEARCH, Inc. <http://www.zresearch.com>
+ This file is part of GlusterFS.
+
+ GlusterFS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3 of the License,
+ or (at your option) any later version.
+
+ GlusterFS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#include "config.h"
+#endif
+
+
+#include "glusterfs.h"
+#include "logging.h"
+#include "dict.h"
+#include "xlator.h"
+#include "defaults.h"
+
+#include <libgen.h>
+
+/* TODO: currently it can work only above posix, no other translators
+ * between them. Not a good thing. Try making more reliable methods.
+ */
+
+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;
+
+struct trash_priv {
+ char trash_dir[ZR_PATH_MAX];
+};
+typedef struct trash_priv trash_private_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 stat *buf);
+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_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_local_t *local = frame->local;
+
+ if (local->loc1.path)
+ loc_wipe (&local->loc1);
+
+ if (local->loc2.path)
+ loc_wipe (&local->loc2);
+
+ STACK_UNWIND (frame, op_ret, op_errno);
+ 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 stat *buf)
+{
+ trash_local_t *local = frame->local;
+
+ if (local->loc1.path)
+ loc_wipe (&local->loc1);
+
+ if (local->loc2.path)
+ loc_wipe (&local->loc2);
+
+ 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_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_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_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_unlink -
+ */
+int32_t
+trash_unlink (call_frame_t *frame,
+ xlator_t *this,
+ loc_t *loc)
+{
+ 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;
+}
+
+/* */
+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_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_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_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_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_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_rename -
+ */
+int32_t
+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 (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_init -
+ */
+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;
+}
+
+void
+fini (xlator_t *this)
+{
+ trash_private_t *priv = this->private;
+ FREE (priv);
+ return;
+}
+
+
+struct xlator_fops fops = {
+ .unlink = trash_unlink,
+ .rename = trash_rename,
+};
+
+struct xlator_mops mops = {
+
+};
+
+struct xlator_cbks cbks = {
+};
+
+struct volume_options options[] = {
+ { .key = { "trash-dir" },
+ .type = GF_OPTION_TYPE_PATH
+ },
+ { .key = {NULL} },
+};