diff options
author | Anoop C S <achiraya@redhat.com> | 2015-02-27 15:14:08 +0530 |
---|---|---|
committer | Vijay Bellur <vbellur@redhat.com> | 2015-03-16 03:36:37 -0700 |
commit | 0ef870741a2f49d47a02725aed13a95335a6e42f (patch) | |
tree | fa7e63dae2c23674d61172650a6785b8502d59b8 /xlators | |
parent | 393cdb2613250031fce92cab8dede9154514f816 (diff) |
Features/trash : Combined patches for trash translator
This is the combined patch set for supporting trash feature.
http://www.gluster.org/community/documentation/index.php/Features/Trash
Current patch includes the following features:
* volume set options for enabling trash globally and
exclusively for internal operations like self-heal
and re-balance
* volume set options for setting the eliminate
path, trash directory path and maximum trashable
file size.
* test script for checking the functionality of the
feature
* brief documentation on different aspects of trash
feature.
Change-Id: Ic7486982dcd6e295d1eba0f4d5ee6d33bf1b4cb3
BUG: 1132465
Signed-off-by: Anoop C S <achiraya@redhat.com>
Signed-off-by: Jiffin Tony Thottan <jthottan@redhat.com>
Reviewed-on: http://review.gluster.org/8312
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators')
-rw-r--r-- | xlators/features/Makefile.am | 2 | ||||
-rw-r--r-- | xlators/features/trash/src/Makefile.am | 2 | ||||
-rw-r--r-- | xlators/features/trash/src/trash-mem-types.h | 3 | ||||
-rw-r--r-- | xlators/features/trash/src/trash.c | 2635 | ||||
-rw-r--r-- | xlators/features/trash/src/trash.h | 36 | ||||
-rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-op-sm.c | 78 | ||||
-rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-volgen.c | 34 | ||||
-rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-volgen.h | 1 | ||||
-rw-r--r-- | xlators/mgmt/glusterd/src/glusterd-volume-set.c | 22 |
9 files changed, 2086 insertions, 727 deletions
diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am index 8093441a043..dcb3cc8e5a7 100644 --- a/xlators/features/Makefile.am +++ b/xlators/features/Makefile.am @@ -1,4 +1,4 @@ SUBDIRS = locks quota read-only mac-compat quiesce marker index barrier \ - protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block snapview-client snapview-server # trash path-converter # filter + protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block snapview-client snapview-server trash # path-converter # filter CLEANFILES = diff --git a/xlators/features/trash/src/Makefile.am b/xlators/features/trash/src/Makefile.am index 5251eb08256..2000359a6a9 100644 --- a/xlators/features/trash/src/Makefile.am +++ b/xlators/features/trash/src/Makefile.am @@ -1,5 +1,5 @@ xlator_LTLIBRARIES = trash.la -xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/testing/features +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features trash_la_LDFLAGS = -module -avoid-version diff --git a/xlators/features/trash/src/trash-mem-types.h b/xlators/features/trash/src/trash-mem-types.h index 0e6ef572fcc..b7cad3ce3a9 100644 --- a/xlators/features/trash/src/trash-mem-types.h +++ b/xlators/features/trash/src/trash-mem-types.h @@ -15,7 +15,8 @@ enum gf_trash_mem_types_ { gf_trash_mt_trash_private_t = gf_common_mt_end + 1, gf_trash_mt_char, - gf_trash_mt_trash_elim_pattern_t, + 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 addeb66a053..4eecaf241c6 100644 --- a/xlators/features/trash/src/trash.c +++ b/xlators/features/trash/src/trash.c @@ -15,82 +15,598 @@ #include "trash.h" #include "trash-mem-types.h" +#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} +/* Common routines used in this translator */ + +/** + * 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) +{ + mode_t mode = 0755; + struct stat sbuf = {0,}; + struct iatt ibuf = {0,}; + int ret = 0; + + ret = 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; +} + +/** + * 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: + if (tmp) + GF_FREE (tmp); + return ret; +} + +/** + * The trash directory path should be append at begining 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) +{ + char trash_path[PATH_MAX] = {0,}; + + strcpy (trash_path, priv_value); + if (internal) + strcat (trash_path, "internal_op/"); + + strcpy (path, trash_path); +} + +/** + * 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 orginal path. + */ +void +remove_trash_path (const char *path, gf_boolean_t internal, char *rem_path) +{ + + rem_path = strchr (path + 1, '/'); + if (internal) + rem_path = strchr (path + 1, '/'); +} + +/** + * Check whether the path includes trash directory or internal op directory + * inside trash. This check is used to make sure that we avoid deletion, + * rename and creation operations from trash directory. + */ +int +check_whether_trash_directory (const char *path, + const char *trash_directory_path) +{ + char tmp_path[PATH_MAX] = {0,}; + char internal_op_path[PATH_MAX] = {0,}; + int ret = 0; + + if (path[strlen(path)-1] == '/') + sprintf (tmp_path, "%s", path); + else + sprintf (tmp_path, "%s/", path); + + copy_trash_path (trash_directory_path, _gf_true, internal_op_path); + ret = strcmp (tmp_path, trash_directory_path) && + strcmp (tmp_path, internal_op_path); + + return ret; +} + +/** + * Checks whether the given path reside under the specified eliminate path + */ +int +check_whether_eliminate_path (trash_elim_path *trav, const char *path) +{ + int match = 0; + + while (trav) { + if (strncmp (path, trav->path, strlen(trav->path)) == 0) { + match++; + break; + } + trav = trav->next; + } + return match; +} + +/** + * Stores the eliminate path into internal eliminate path structure + */ +int +store_eliminate_path (char *str, trash_elim_path *eliminate) +{ + trash_elim_path *trav = NULL; + char *component = NULL; + char elm_path[PATH_MAX] = {0,}; + int ret = 0; + char *strtokptr = NULL; + + 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] != '/') + strcat (elm_path, "/"); + + trav->path = gf_strdup(elm_path); + if (!trav->path) { + ret = ENOMEM; + gf_log ("trash", GF_LOG_DEBUG, "out of memory"); + goto out; + } + trav->next = eliminate; + eliminate = trav; + component = strtok_r (NULL, ",", &strtokptr); + } +out: + return ret; +} + +/** + * Appends time stamp to given string + */ +void +append_time_stamp (char *name) +{ + int i; + char timestr[64] = {0,}; + + gf_time_fmt (timestr, sizeof(timestr), time (NULL), + gf_timefmt_F_HMS); + + /* removing white spaces in timestamp */ + for (i = 0; i < strlen (timestr); i++) { + if (timestr[i] == ' ') + timestr[i] = '_'; + } + strcat (name, "_"); + strcat (name, timestr); +} + +/** + * Wipe the memory used by trash location variable + */ +void +trash_local_wipe (trash_local_t *local) +{ + GF_VALIDATE_OR_GOTO ("trash", local, 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; +} + +/** + * Wipe the memory used by eliminate path through a + * recursive call + */ +void +wipe_eliminate_path (trash_elim_path *trav) +{ + if (trav) { + wipe_eliminate_path (trav->next); + GF_FREE (trav->path); + GF_FREE (trav); + } +} + 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); + struct iatt *stbuf, struct iobref *iobuf, + dict_t *xdata); 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); + 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); + struct iatt *postparent, dict_t *xdata); + +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, 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); + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata); -void -trash_local_wipe (trash_local_t *local) +/** + * This getxattr calls returns existing trash directory path in + * the dictionary + */ +int32_t +trash_notify_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) { - if (!local) + data_t *data = NULL; + trash_private_t *priv = NULL; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO ("trash", priv, out); + + data = dict_get (dict, GET_ANCESTRY_PATH_KEY); + if (!data) { + gf_log (this->name, GF_LOG_DEBUG, + "oldtrash-directory doesnot exists"); + priv->oldtrash_dir = gf_strdup (priv->newtrash_dir); + if (!priv->oldtrash_dir) { + gf_log (this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + } else { + priv->oldtrash_dir = gf_strdup (data->data); + if (!priv->oldtrash_dir) { + gf_log (this->name, GF_LOG_ERROR, "out of memory"); + ret = ENOMEM; + goto out; + } + gf_log (this->name, GF_LOG_DEBUG, "old trash directory" + " path is %s", data->data); + } + +out: + return 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_notify_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; + loc_t loc = {0,}; + int ret = 0; + + priv = this->private; + GF_VALIDATE_OR_GOTO ("trash", priv, out); + + if (op_ret == 0) { + + gf_log (this->name, GF_LOG_DEBUG, "inode found with gfid %s", + uuid_utoa(buf->ia_gfid)); + + uuid_copy (loc.gfid, trash_gfid); + + /* Find trash inode using available information */ + priv->trash_inode = inode_link (inode, NULL, NULL, buf); + + loc.inode = inode_ref (priv->trash_inode); + + /*Used to find path of old trash directory*/ + STACK_WIND (frame, trash_notify_getxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->getxattr, &loc, + GET_ANCESTRY_PATH_KEY, xdata); + } + + /* If there is no old trash directory we set its value to new one, + * which is the valid condition for trash directory creation + */ + else { + priv->oldtrash_dir = gf_strdup (priv->newtrash_dir); + if (!priv->oldtrash_dir) { + gf_log (this->name, GF_LOG_ERROR, "out of memory"); + goto out; + } + } + +out: + loc_wipe (&loc); + return ret; +} + +int32_t +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) +{ + if (op_ret != 0) + gf_log (this->name, GF_LOG_ERROR, "mkdir failed for " + "internal op directory : %s", strerror (op_errno)); + return op_ret; +} + +/** + * This is the call back of mkdir fop initated using STACK_WIND in + * notify function which is used to create trash directory in the brick + * when a volume starts.The frame of the mkdir must destroyed from + * this function itself since it was created by trash xlator + */ +int32_t +trash_notify_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) +{ + uuid_t *gfid_ptr = NULL; + loc_t loc = {0, }; + int ret = 0; + dict_t *dict = NULL; + char internal_op_path[PATH_MAX] = {0,}; + trash_private_t *priv = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO ("trash", priv, out); + + dict = dict_new (); + if (!dict) { + ret = -1; goto out; + } + if ((op_ret == 0) || (op_ret == -1 && op_errno == EEXIST)) { + gfid_ptr = GF_CALLOC (1, sizeof(uuid_t), + gf_common_mt_uuid_t); + if (!gfid_ptr) { + ret = ENOMEM; + goto out; + } + uuid_copy (*gfid_ptr, internal_op_gfid); - loc_wipe (&local->loc); - loc_wipe (&local->newloc); + uuid_copy (loc.gfid, internal_op_gfid); + uuid_copy (loc.pargfid, trash_gfid); + loc.name = gf_strdup ("internal_op"); - if (local->fd) - fd_unref (local->fd); + 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); - if (local->newfd) - fd_unref (local->newfd); + loc.path = gf_strdup (internal_op_path); - mem_put (local); + if (!loc.path) { + gf_log (this->name, GF_LOG_DEBUG, + "out of memory"); + ret = ENOMEM; + goto out; + } + + loc.inode = inode_new (priv->trash_itable); + loc.inode->ia_type = IA_IFDIR; + /* Fixed gfid is set for trash directory with + * this function + */ + ret = dict_set_dynptr (dict, "gfid-req", gfid_ptr, + sizeof (uuid_t)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "setting key gfid-req failed"); + goto out; + } + + /* The mkdir call for creating trash directory */ + STACK_WIND (frame, trash_internal_op_mkdir_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &loc, 0755, + 0022, dict); + /* After creating we must call other notify functions */ + default_notify (this, GF_EVENT_CHILD_UP, NULL); + } else { + gf_log (this->name, GF_LOG_ERROR, "mkdir failed for trash" + " directory : %s", strerror (op_errno)); + } + + STACK_DESTROY (frame->root); out: - return; + if (ret && gfid_ptr) + GF_FREE (gfid_ptr); + if (dict) + dict_unref (dict); + return 0; +} + +int32_t +trash_notify_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) +{ + if ((op_ret == 0) || (op_ret == -1 && op_errno == EEXIST)) { + /* After creating we must call other notify functions */ + default_notify (this, GF_EVENT_CHILD_UP, NULL); + } else { + gf_log (this->name, GF_LOG_ERROR, "rename failed: %s", + strerror (op_errno)); + } + + STACK_DESTROY (frame->root); + return op_ret; +} + +/** + * This is the call back of rename fop initated using STACK_WIND in + * reconfigure function which is used to rename trash directory in + * the brick when we perform volume set.This frame must destroyed + * from this function itself since it was created by trash xlator + */ +int32_t +trash_reconf_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) +{ + if (op_ret == -1 && op_errno == EEXIST) { + + gf_log (this->name, GF_LOG_ERROR, "rename failed: %s", + strerror (op_errno)); + } + + STACK_DESTROY (frame->root); + + return op_ret; } int32_t +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) +{ + STACK_UNWIND_STRICT (mkdir, frame, op_ret, op_errno, inode, + buf, preparent, postparent, xdata); + return 0; +} + +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; +} + +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; +} + +/** + * 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) + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) { - TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, preparent, postparent); + TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, preparent, + postparent, xdata); 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 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) + struct iatt *postparent, 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 loop_count = 0; - int i = 0; - loc_t tmp_loc = {0,}; + 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); + 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) { @@ -102,31 +618,53 @@ trash_unlink_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, break; tmp_dirname = strchr (tmp_str + count + 1, '/'); } - tmp_path = memdup (local->newpath, count); + 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'; - tmp_loc.path = tmp_path; + 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; + } - /* TODO:create the directory with proper permissions */ + /* 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; + } + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); STACK_WIND_COOKIE (frame, trash_unlink_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - + 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) { + 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, - this->children->xlator, - this->children->xlator->fops->rename, - &local->loc, &tmp_loc); + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, + &local->loc, &tmp_loc, xdata); goto out; } } @@ -136,7 +674,10 @@ trash_unlink_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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) @@ -146,492 +687,356 @@ trash_unlink_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, break; tmp_dirname = strchr (tmp_str + count + 1, '/'); } - tmp_path = memdup (local->newpath, count); + 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; } - tmp_loc.path = tmp_path; + + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); STACK_WIND_COOKIE (frame, trash_unlink_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &tmp_loc, + get_permission(real_path), 0022, xdata); out: - GF_FREE (cookie); - GF_FREE (tmp_str); - - return 0; + if (tmp_path) + GF_FREE (tmp_path); + if (tmp_str) + GF_FREE (tmp_str); + return ret; } -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); - +/** + * The name of unlinking file should be renamed as starting + * from trash directory as mentioned in the mount point + */ 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 iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) { - trash_local_t *local = NULL; - char *tmp_str = NULL; - char *dir_name = NULL; - char *tmp_cookie = NULL; - loc_t tmp_loc = {0,}; + 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,}; + 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 doesnot exists 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); + dir_name = dirname (tmp_str); /* stores directory name */ - tmp_loc.path = dir_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; } - /* TODO: create the directory with proper permissions */ + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_str, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); + /* 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; + &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, - this->children->xlator, - this->children->xlator->fops->unlink, &local->loc); + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, + &local->loc, 0, xdata); - return 0; + 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); - return 0; + FIRST_CHILD(this)->fops->unlink, + &local->loc, 0, xdata); + goto out; } /* All other cases, unlink should return success */ TRASH_STACK_UNWIND (unlink, frame, 0, op_errno, &local->preparent, - &local->postparent); + &local->postparent, xdata); +out: + if (tmp_str) + GF_FREE (tmp_str); + if (tmp_cookie) + GF_FREE (tmp_cookie); - return 0; + return ret; } - - +/** + * move backs from trash translator to truncate call + */ 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) + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) { - TRASH_STACK_UNWIND (truncate, frame, op_ret, op_errno, prebuf, postbuf); + TRASH_STACK_UNWIND (truncate, frame, op_ret, op_errno, prebuf, + postbuf, xdata); 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_unlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf) + 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,}; + 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 (-1 == op_ret) { + if (op_ret == -1) { gf_log (this->name, GF_LOG_DEBUG, "%s: %s", local->loc.path, strerror (op_errno)); - goto fail; + TRASH_STACK_UNWIND (unlink, frame, op_ret, op_errno, buf, + NULL, xdata); + ret = -1; + goto out; } - 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 the file is too big just unlink it */ - if (buf->ia_size > priv->max_trash_file_size) { - gf_log (this->name, GF_LOG_DEBUG, + 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; - -} - -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) -{ - 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"); + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, &local->loc, + 0, xdata); 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); - } - + /* 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; } - 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); - } + STACK_WIND (frame, trash_unlink_rename_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, + &local->loc, &new_loc, xdata); out: - GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ - GF_FREE (tmp_str); - - return 0; -} - -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_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; - } + loc_wipe (&new_loc); - tmp_loc.path = local->newpath; + return ret; - STACK_WIND (frame, trash_rename_rename_cbk, - this->children->xlator, - this->children->xlator->fops->rename, - &local->newloc, &tmp_loc); - - return 0; } - +/** + * Unlink is called internally by rm system call and also + * by internal operations of gluster such as self-heal + */ int32_t -trash_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, - loc_t *newloc) +trash_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int xflags, + dict_t *xdata) { - trash_elim_pattern_t *trav = NULL; - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - char timestr[64] = {0,}; - int32_t match = 0; + trash_private_t *priv = NULL; + trash_local_t *local = NULL;/* files inside trash */ + int32_t match = 0; + char *pathbuf = NULL; + int ret = 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); + GF_VALIDATE_OR_GOTO ("trash", priv, out); - 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; + /* 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; } - 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); + /* 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 */ + uuid_copy (loc->gfid, loc->inode->gfid); - /* 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_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) -{ - 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; - } + /* Checking for valid location */ + if (uuid_is_null (loc->gfid) && 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; } - if ((strncmp (loc->path, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || (match)) { + /* 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: file matches eliminate pattern, " - "not moved to trash", loc->name); - } else { - /* unlink from the trash-dir, not keeping any copy */ - ; + "%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, - this->children->xlator, - this->children->xlator->fops->unlink, loc); - return 0; + 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); - return 0; + TRASH_STACK_UNWIND (unlink, frame, -1, ENOMEM, NULL, NULL, + xdata); + ret = ENOMEM; + goto out; } frame->local = local; loc_copy (&local->loc, loc); - strcpy (local->origpath, loc->path); - strcpy (local->newpath, priv->trash_dir); - strcat (local->newpath, loc->path); + /* rename new location of file as starting from trash directory */ + strcpy (local->origpath, pathbuf); + copy_trash_path (priv->newtrash_dir, (frame->root->pid < 0), + local->newpath); + strcat (local->newpath, pathbuf); - { - /* 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); - } + /* append timestamp to file name so that we can avoid + * name collisions inside trash + */ + append_time_stamp (local->newpath); LOCK_INIT (&frame->lock); STACK_WIND (frame, trash_unlink_stat_cbk, - this->children->xlator, - this->children->xlator->fops->stat, loc); - - return 0; + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->stat, loc, xdata); +out: + return ret; } +/** + * Use this when a failure occurs, and delete the newly created file + */ 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) + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) { - /* use this Function when a failure occurs, and - delete the newly created file. */ - trash_local_t *local = NULL; + trash_local_t *local = NULL; local = frame->local; + GF_VALIDATE_OR_GOTO ("trash", local, out); if (op_ret == -1) { gf_log (this->name, GF_LOG_DEBUG, @@ -641,20 +1046,26 @@ trash_truncate_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, - &local->loc, local->fop_offset); - + &local->loc, local->fop_offset, xdata); +out: return 0; } +/** + * Read from source file + */ 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) + struct iatt *stbuf, struct iobref *iobuf, + dict_t *xdata) { - trash_local_t *local = NULL; + + trash_local_t *local = NULL; local = frame->local; + GF_VALIDATE_OR_GOTO ("trash", local, out); if (op_ret == -1) { gf_log (this->name, GF_LOG_DEBUG, @@ -663,28 +1074,34 @@ trash_truncate_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, STACK_WIND (frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, - &local->newloc); + &local->newloc, 0, xdata); 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); + local->newfd, vector, count, local->cur_offset, 0, iobuf, + xdata); out: return 0; } +/** + * Write to file created in trash directory + */ 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) + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) { - trash_local_t *local = NULL; + 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. */ @@ -693,7 +1110,8 @@ trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, strerror (op_errno)); STACK_WIND (frame, trash_truncate_unlink_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->unlink, &local->newloc); + FIRST_CHILD(this)->fops->unlink, &local->newloc, 0, + xdata); goto out; } @@ -703,7 +1121,7 @@ trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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); + local->cur_offset, 0, xdata); goto out; } @@ -711,86 +1129,115 @@ trash_truncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, /* 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); + local->fop_offset, xdata); out: return 0; } - - +/** + * The source file is opened for reading and writing + */ 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) + int32_t op_ret, int32_t op_errno, fd_t *fd, + dict_t *xdata) { - trash_local_t *local = NULL; + 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. + /* 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); + &local->newloc, 0, xdata); goto out; } - local->cur_offset = local->fop_offset; + local->cur_offset = 0; 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); + local->fd, (size_t)GF_BLOCK_READV_SIZE, local->cur_offset, + 0, xdata); out: return 0; } - +/** + * Creates new file descriptor for read and write operations, + * if the path is present in trash directory + */ 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) + 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,}; + 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); + + /* Checks whether path is present in trash directory or not */ if ((op_ret == -1) && (op_errno == ENOENT)) { - //Creating the directory structure here. + /* 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); tmp_path = gf_strdup (dir_name); if (!tmp_path) { gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + goto out; } - tmp_loc.path = tmp_path; - - /* TODO: create the directory with proper permissions */ + 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; + } + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); + /* 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); + &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. + /* 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)); @@ -798,47 +1245,66 @@ trash_truncate_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, &local->loc, - local->fop_offset); + local->fop_offset, xdata); goto out; } 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; } +/** + * 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_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 iatt *postparent, 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,}; + 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; - local = frame->local; - if (!local) - goto out; + 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; tmp_str = gf_strdup (local->newpath); if (!tmp_str) { gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + ret = ENOMEM; goto out; } @@ -853,16 +1319,39 @@ trash_truncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, break; tmp_dirname = strchr (tmp_str + count + 1, '/'); } - tmp_path = memdup (local->newpath, count); + 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_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); + 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; + } + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); + 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; } @@ -870,14 +1359,16 @@ trash_truncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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. + strcpy (real_path, priv->brick_path); + strcat (real_path, local->origpath); + /* Call create again once directory structure + is created. */ STACK_WIND (frame, trash_truncate_create_cbk, - FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->create, &local->newloc, flags, - st_mode_from_ia (prot, local->loc.inode->ia_type), - local->newfd, NULL); + get_permission (real_path), + 0022, local->newfd, xdata); goto out; } } @@ -893,82 +1384,147 @@ trash_truncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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); + 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_loc.path = tmp_path; + 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; + } + + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); STACK_WIND_COOKIE (frame, trash_truncate_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &tmp_loc, + get_permission(real_path), + 0022, xdata); out: - GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ - GF_FREE (tmp_str); + if (tmp_str) + GF_FREE (tmp_str); + if (tmp_path) + GF_FREE (tmp_path); - return 0; + return ret; } 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) + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) { - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - char timestr[64] = {0,}; - char loc_newname[PATH_MAX] = {0,}; - int32_t flags = 0; + 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); - return 0; + TRASH_STACK_UNWIND (truncate, frame, op_ret, op_errno, buf, + NULL, xdata); + goto out; } - - 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, " + /* 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; + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, + &local->loc, local->fop_offset, xdata); + goto out; } - strcpy (local->newpath, priv->trash_dir); + /* Retrives 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); 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); + /* append timestamp to file name so that we can avoid + name collisions inside trash */ + append_time_stamp (local->newpath); + strcpy (loc_newname, local->loc.name); + append_time_stamp (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 vaild parent and pargfids for both files */ + + local->loc.parent = inode_ref (dir_entry->parent); + uuid_copy (local->loc.pargfid, dir_entry->parent->gfid); + + local->newloc.parent = inode_ref (dir_entry->parent); + uuid_copy (local->newloc.pargfid, dir_entry->parent->gfid); + flags = O_CREAT|O_EXCL|O_WRONLY; STACK_WIND (frame, trash_truncate_create_cbk, @@ -976,45 +1532,73 @@ trash_truncate_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, FIRST_CHILD(this)->fops->create, &local->newloc, flags, st_mode_from_ia (buf->ia_prot, local->loc.inode->ia_type), - local->newfd, NULL); + 0022, local->newfd, xdata); - return 0; +out: + return ret; } +/** + * 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) + off_t offset, dict_t *xdata) { - trash_elim_pattern_t *trav = NULL; - trash_private_t *priv = NULL; - trash_local_t *local = NULL; - int32_t match = 0; + trash_private_t *priv = NULL; + trash_local_t *local = NULL; + int32_t match = 0; + char *pathbuf = NULL; + int ret = 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; - } + 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); - if ((strncmp (loc->path, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || (offset) || (match)) { + /* 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'", loc->path); + "'eliminate path'", loc->path); } - // Trying to truncate from the trash can dir, - // do the actual truncate without moving to trash-dir. + /* 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); + FIRST_CHILD(this)->fops->truncate, loc, offset, + xdata); goto out; } @@ -1023,60 +1607,66 @@ trash_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, 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; + TRASH_STACK_UNWIND (truncate, frame, -1, ENOMEM, NULL, NULL, + xdata); + ret = ENOMEM; + goto out; } loc_copy (&local->loc, loc); - + local->loc.path = pathbuf; local->fop_offset = offset; frame->local = local; STACK_WIND (frame, trash_truncate_stat_cbk, - this->children->xlator, - this->children->xlator->fops->stat, loc); + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->stat, loc, + xdata); out: - return 0; + return ret; } 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) + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) { - trash_local_t *local = NULL; + trash_local_t *local = NULL; local = frame->local; + GF_VALIDATE_OR_GOTO ("trash", local, out); 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); - + local->fd, local->fop_offset, xdata); +out: 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) + struct iatt *prebuf, struct iatt *postbuf, + dict_t *xdata) { - trash_local_t *local = NULL; + trash_local_t *local = NULL; local = frame->local; + GF_VALIDATE_OR_GOTO ("trash", local, out); if (op_ret == -1) { STACK_WIND (frame, trash_ftruncate_unlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, - &local->newloc); + &local->newloc, 0, xdata); return 0; } @@ -1085,14 +1675,14 @@ trash_ftruncate_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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); + local->cur_offset, 0, xdata); return 0; } STACK_WIND (frame, trash_common_unwind_buf_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->ftruncate, local->fd, - local->fop_offset); - + local->fop_offset, xdata); +out: return 0; } @@ -1101,24 +1691,28 @@ 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) + struct iatt *stbuf, struct iobref *iobuf, + dict_t *xdata) { - trash_local_t *local = NULL; + trash_local_t *local = NULL; local = frame->local; + GF_VALIDATE_OR_GOTO ("trash", local, out); + 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); + &local->newloc, 0, xdata); 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); - + local->newfd, vector, count, local->cur_offset, 0, + NULL, xdata); +out: return 0; } @@ -1127,49 +1721,77 @@ 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) + 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; - loc_t tmp_loc = {0,}; + trash_local_t *local = NULL; + char *tmp_str = NULL; + char *dir_name = NULL; + char *tmp_path = NULL; + loc_t tmp_loc = {0,}; + char *pathbuf = NULL; + 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); + + /* This will be more accurate */ + inode_path (fd->inode, NULL, &pathbuf); 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"); + goto out; } dir_name = dirname (tmp_str); tmp_path = gf_strdup (dir_name); if (!tmp_path) { gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + goto out; } - tmp_loc.path = tmp_path; - - /* TODO: create the directory with proper permissions */ + 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; + } + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); + /* 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; + &tmp_loc, get_permission(real_path), + 0022, xdata); + loc_wipe (&tmp_loc); + goto out; } 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; + local->fd, local->fop_offset, xdata); + goto out; } 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); + (size_t)GF_BLOCK_READV_SIZE, local->cur_offset, 0, xdata); +out: + if (tmp_str) + GF_FREE (tmp_str); + if (tmp_path) + GF_FREE (tmp_path); return 0; } @@ -1179,28 +1801,35 @@ 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) + struct iatt *postparent, 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,}; + 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; - if (!local) - goto out; + GF_VALIDATE_OR_GOTO ("trash", priv, 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"); + ret = ENOMEM; goto out; } @@ -1215,32 +1844,48 @@ trash_ftruncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, break; tmp_dirname = strchr (tmp_str + count + 1, '/'); } - tmp_path = memdup (local->newpath, count); + 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; } - tmp_loc.path = tmp_path; + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); STACK_WIND_COOKIE (frame, trash_ftruncate_mkdir_cbk, - tmp_path, this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); - + 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) { - ia_prot_t prot = {0, }; + if (strcmp ((char *)cookie, dir_name) == 0) { flags = O_CREAT|O_EXCL|O_WRONLY; - - //Call create again once directory structure is created. + strcpy (real_path, priv->brick_path); + strcat (real_path, local->origpath); + /* 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); + get_permission (real_path), + 0022, local->newfd, xdata); goto out; } } @@ -1250,288 +1895,864 @@ trash_ftruncate_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, 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); + 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_loc.path = tmp_path; + tmp_path[count] = '\0'; - STACK_WIND_COOKIE (frame, trash_ftruncate_mkdir_cbk, tmp_path, - this->children->xlator, - this->children->xlator->fops->mkdir, - &tmp_loc, 0755, NULL); + 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; + } + strcpy (real_path, priv->brick_path); + remove_trash_path (tmp_path, (frame->root->pid < 0), tmp_stat); + if (tmp_stat) + strcat (real_path, tmp_stat); + STACK_WIND_COOKIE (frame, trash_ftruncate_mkdir_cbk, tmp_path, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &tmp_loc, + get_permission(real_path), + 0022, xdata); out: - GF_FREE (cookie); /* strdup (dir_name) was sent here :) */ - GF_FREE (tmp_str); + if (tmp_str) + GF_FREE (tmp_str); + if (tmp_path) + GF_FREE (tmp_path); - return 0; + 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) + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) { trash_private_t *priv = NULL; trash_local_t *local = NULL; 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->newloc.path, strerror(op_errno)); - TRASH_STACK_UNWIND (ftruncate, frame, -1, op_errno, buf, NULL); - return 0; + TRASH_STACK_UNWIND (ftruncate, frame, -1, op_errno, buf, + NULL, xdata); + goto out; } - if ((buf->ia_size == 0) || (buf->ia_size > priv->max_trash_file_size)) + if ((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; + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, + local->fd, local->fop_offset, xdata); + goto out; } - STACK_WIND (frame, trash_ftruncate_create_cbk, FIRST_CHILD(this), + STACK_WIND (frame, trash_truncate_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); + 0022, local->newfd, xdata); +out: return 0; } +/** + * 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) +trash_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, + dict_t *xdata) { - 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; + 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; + } - dir_entry = __dentry_search_arbit (fd->inode); + /* 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); - if (priv->eliminate) { - trav = priv->eliminate; - while (trav) { - if (fnmatch(trav->pattern, dir_entry->name, 0) == 0) { - match++; - break; - } - trav = trav->next; + /* 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); } - } - if ((strncmp (pathbuf, priv->trash_dir, - strlen (priv->trash_dir)) == 0) || - (offset >= priv->max_trash_file_size) || - (!retval) || - match) { + /* 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); - return 0; + 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); - return 0; + TRASH_STACK_UNWIND (ftruncate, frame, -1, ENOMEM, NULL, + NULL, xdata); + ret = -1; + goto out; } - 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); + /* To convert fd to location */ frame->local=local; - local->newloc.inode = newinode; - local->newloc.path = local->newpath; - - local->loc.inode = inode_ref (fd->inode); local->loc.path = pathbuf; + local->loc.inode = inode_ref (fd->inode); + uuid_copy (local->loc.gfid, local->loc.inode->gfid); local->fop_offset = offset; - local->cur_offset = offset; - STACK_WIND (frame, trash_ftruncate_fstat_cbk, this->children->xlator, - this->children->xlator->fops->fstat, fd); + /* 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_mkdir (call_frame_t *frame, xlator_t *this, + loc_t *loc, mode_t mode, mode_t umask, 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_trash_directory (loc->path, priv->newtrash_dir)) { + 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); + } +out: return 0; } /** - * trash_init - + * 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_trash_directory (oldloc->path, priv->newtrash_dir)) { + 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); + } +out: + return 0; +} + +/** + * The rmdir call is intercepted to avoid deletion of + * trash directory in the mount by the user + */ +int32_t +trash_rmdir (call_frame_t *frame, xlator_t *this, + loc_t *loc, int flags, 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_trash_directory (loc->path, priv->newtrash_dir)) { + 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); + } +out: + return 0; +} + +/** + * Volume set option is handled by the reconfigure funtion. + * 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; + loc_t old_loc = {0, }; + loc_t new_loc = {0, }; + call_frame_t *frame = NULL; + char trash_dir[PATH_MAX] = {0,}; + + priv = this->private; + GF_VALIDATE_OR_GOTO ("trash", priv, out); + + GF_OPTION_RECONF ("trash", priv->state, options, bool, out); + + GF_OPTION_RECONF ("trash-dir", tmp, options, str, out); + if (tmp) { + sprintf(trash_dir, "/%s/", tmp); + 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; + } + 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; + } + frame = create_frame (this, this->ctx->pool); + + /* assign new location values to new_loc members */ + uuid_copy (new_loc.gfid, trash_gfid); + uuid_copy (new_loc.pargfid, root_gfid); + ret = extract_trash_directory (priv->newtrash_dir, + &new_loc.name); + if (ret) { + gf_log (this->name, GF_LOG_DEBUG, + "out of memory"); + goto out; + } + new_loc.path = gf_strdup (priv->newtrash_dir); + if (!new_loc.path) { + ret = ENOMEM; + gf_log (this->name, GF_LOG_DEBUG, + "out of memory"); + goto out; + } + + /* assign old location values to old_loc members */ + uuid_copy (old_loc.gfid, trash_gfid); + 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); + uuid_copy(old_loc.inode->gfid, old_loc.gfid); + + STACK_WIND (frame, trash_reconf_rename_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, + &old_loc, &new_loc, options); + if (priv->oldtrash_dir) + GF_FREE (priv->oldtrash_dir); + + priv->oldtrash_dir = gf_strdup(priv->newtrash_dir); + if (!priv->oldtrash_dir) { + ret = ENOMEM; + gf_log (this->name, GF_LOG_DEBUG, + "out of memory"); + goto out; + } + } + } + tmp = NULL; + + GF_OPTION_RECONF ("trash-internal-op", priv->internal, options, + bool, out); + + GF_OPTION_RECONF ("trash-max-filesize", max_fsize, options, + size_uint64, out); + if (max_fsize) { + if (max_fsize > 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_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); + + } + +out: + if (tmp_str) + GF_FREE (tmp_str); + loc_wipe (&new_loc); + loc_wipe (&old_loc); + + return ret; +} + +/** + * 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; + dict_t *dict = NULL; + int ret = 0; + uuid_t *tgfid_ptr = NULL; + loc_t loc = {0, }; + loc_t old_loc = {0, }; + call_frame_t *frame = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO ("trash", priv, out); + + /* Check whether posix is up not */ + if (event == GF_EVENT_CHILD_UP) { + frame = create_frame(this, this->ctx->pool); + dict = dict_new (); + if (!dict) { + ret = ENOMEM; + goto out; + } + priv->trash_itable = inode_table_new (0, this); + + /* Here there is two possiblities ,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) { + loc.inode = inode_new (priv->trash_itable); + uuid_copy (loc.gfid, trash_gfid); + + gf_log (this->name, GF_LOG_DEBUG, "nameless lookup for" + "old trash directory"); + STACK_WIND (frame, trash_notify_lookup_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, + &loc, dict); + gf_log (this->name, GF_LOG_DEBUG, "old_trash_dir %s", + priv->oldtrash_dir); + loc_wipe (&loc); + } + + if (strcmp (priv->oldtrash_dir, priv->newtrash_dir) == 0) { + gf_log (this->name, GF_LOG_DEBUG, "Creating trash " + "directory %s from notify", + priv->newtrash_dir); + + tgfid_ptr = GF_CALLOC (1, sizeof(uuid_t), + gf_common_mt_uuid_t); + if (!tgfid_ptr) { + ret = ENOMEM; + goto out; + } + uuid_copy (*tgfid_ptr, trash_gfid); + + uuid_copy (loc.gfid, trash_gfid); + 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); + + /* Fixed gfid is set for trash directory with + * this function + */ + ret = dict_set_dynptr (dict, "gfid-req", tgfid_ptr, + sizeof (uuid_t)); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, + "setting key gfid-req failed"); + goto out; + } + + /* The mkdir call for creating trash directory */ + STACK_WIND (frame, trash_notify_mkdir_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, &loc, 0755, + 0022, dict); + } else { + /* assign new location values to new_loc members */ + gf_log (this->name, GF_LOG_DEBUG, "Renaming %s -> %s" + " from notify", priv->oldtrash_dir, + priv->newtrash_dir); + uuid_copy (loc.gfid, trash_gfid); + 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; + } + /* assign old location values to old_loc members */ + uuid_copy (old_loc.gfid, trash_gfid); + 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) { + gf_log (this->name, GF_LOG_DEBUG, + "out of memory"); + ret = ENOMEM; + goto out; + } + + old_loc.inode = inode_ref (priv->trash_inode); + uuid_copy(old_loc.inode->gfid, old_loc.gfid); + + STACK_WIND (frame, trash_notify_rename_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, + &old_loc, &loc, dict); + if (priv->oldtrash_dir) + GF_FREE (priv->oldtrash_dir); + + priv->oldtrash_dir = gf_strdup(priv->newtrash_dir); + if (!priv->oldtrash_dir) { + gf_log (this->name, GF_LOG_DEBUG, + "out of memory"); + ret = ENOMEM; + goto out; + } + } + } else { + ret = default_notify (this, event, data); + if (ret) + gf_log (this->name, GF_LOG_INFO, + "default notify event failed"); + } + +out: + if (ret && tgfid_ptr) + GF_FREE (tgfid_ptr); + if (dict) + dict_unref (dict); + loc_wipe (&loc); + loc_wipe (&old_loc); + + return ret; +} + +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + GF_VALIDATE_OR_GOTO ("trash", this, out); + + 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 init (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 */ + 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"); - return -1; + ret = -1; + goto out; } if (!this->parents) { gf_log (this->name, GF_LOG_WARNING, - "dangling volume. check volfile "); + "dangling volume. check volfile"); } - _priv = GF_CALLOC (1, sizeof (*_priv), gf_trash_mt_trash_private_t); - if (!_priv) { + 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; + ret = ENOMEM; + goto out; } - data = dict_get (this->options, "trash-dir"); - if (!data) { + /* 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->trash_dir = gf_strdup ("/.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 { - /* 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); + 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; - data = dict_get (this->options, "eliminate-pattern"); - if (!data) { - gf_log (this->name, GF_LOG_TRACE, + 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 (data->data); + tmp_str = gf_strdup (tmp); if (!tmp_str) { - gf_log (this->name, GF_LOG_DEBUG, "out of memory"); + gf_log (this->name, GF_LOG_ERROR, + "out of memory"); + ret = ENOMEM; + goto out; } + ret = store_eliminate_path (tmp_str, priv->eliminate); - /* 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); - } } + tmp = NULL; - /* TODO: do gf_string2sizet () */ - data = dict_get (this->options, "max-trashable-file-size"); - if (!data) { - gf_log (this->name, GF_LOG_DEBUG, + 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; + 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; + priv->max_trash_file_size = GF_ALLOWED_MAX_FILE_SIZE; } else - _priv->max_trash_file_size = max_trash_file_size64; + 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); + 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"); - return -1; + 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; } + gf_log (this->name, GF_LOG_DEBUG, "brick path is%s", priv->brick_path); - this->private = (void *)_priv; - return 0; + this->private = (void *)priv; + ret = 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); + } + return ret; } +/** + * trash_fini + */ void fini (xlator_t *this) { trash_private_t *priv = NULL; + GF_VALIDATE_OR_GOTO ("trash", this, out); priv = this->private; - GF_FREE (priv); + 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->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 volume_options options[] = { - { .key = { "trash-directory" }, - .type = GF_OPTION_TYPE_PATH, + { .key = { "trash" }, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "Enable/disable trash translator", + }, + { .key = { "trash-dir" }, + .type = GF_OPTION_TYPE_STR, + .default_value = ".trashcan", + .description = "Directory for trash files", + }, + { .key = { "trash-eliminate-path" }, + .type = GF_OPTION_TYPE_STR, + .description = "Eliminate paths to be excluded " + "from trashing", }, - { .key = { "eliminate-pattern" }, - .type = GF_OPTION_TYPE_STR, + { .key = { "trash-max-filesize" }, + .type = GF_OPTION_TYPE_SIZET, + .default_value = "5MB", + .description = "Maximum size of file that can be " + "moved to trash", }, - { .key = { "max-trashable-file-size" }, - .type = GF_OPTION_TYPE_SIZET, + { .key = { "trash-internal-op" }, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "Enable/disable trash translator for " + "internal operations", }, - { .key = {NULL} }, + { .key = {NULL} }, }; diff --git a/xlators/features/trash/src/trash.h b/xlators/features/trash/src/trash.h index 9a7c033617d..3e03edf5474 100644 --- a/xlators/features/trash/src/trash.h +++ b/xlators/features/trash/src/trash.h @@ -37,7 +37,6 @@ #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 */ @@ -54,26 +53,31 @@ struct trash_struct { }; 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; + 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_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__ */ diff --git a/xlators/mgmt/glusterd/src/glusterd-op-sm.c b/xlators/mgmt/glusterd/src/glusterd-op-sm.c index a78c80eceb4..2f121e49e9e 100644 --- a/xlators/mgmt/glusterd/src/glusterd-op-sm.c +++ b/xlators/mgmt/glusterd/src/glusterd-op-sm.c @@ -647,11 +647,14 @@ glusterd_op_stage_set_volume (dict_t *dict, char **op_errstr) char *key = NULL; char *key_fixed = NULL; char *value = NULL; + char *val_dup = NULL; char str[100] = {0, }; + char *trash_path = NULL; int count = 0; int dict_count = 0; char errstr[2048] = {0, }; glusterd_volinfo_t *volinfo = NULL; + glusterd_brickinfo_t *brickinfo = NULL; dict_t *val_dict = NULL; gf_boolean_t global_opt = _gf_false; glusterd_volinfo_t *voliter = NULL; @@ -663,7 +666,9 @@ glusterd_op_stage_set_volume (dict_t *dict, char **op_errstr) uint32_t local_key_op_version = 0; gf_boolean_t origin_glusterd = _gf_true; gf_boolean_t check_op_version = _gf_true; + gf_boolean_t trash_enabled = _gf_false; gf_boolean_t all_vol = _gf_false; + struct stat stbuf = {0, }; GF_ASSERT (dict); this = THIS; @@ -940,6 +945,76 @@ glusterd_op_stage_set_volume (dict_t *dict, char **op_errstr) if (glusterd_check_globaloption (key)) global_opt = _gf_true; + if (volinfo) { + ret = glusterd_volinfo_get (volinfo, + VKEY_FEATURES_TRASH, &val_dup); + if (val_dup) { + ret = gf_string2boolean (val_dup, + &trash_enabled); + if (ret) + goto out; + } + } + + if (!strcmp(key, "features.trash-dir") && trash_enabled) { + if (strchr (value, '/')) { + snprintf (errstr, sizeof (errstr), + "Path is not allowed as option"); + gf_log (this->name, GF_LOG_ERROR, + "Unable to set the options in 'volume " + "set': %s", errstr); + ret = -1; + goto out; + } + + list_for_each_entry (brickinfo, &volinfo->bricks, + brick_list) { + /* Check for local brick */ + if (!uuid_compare (brickinfo->uuid, MY_UUID)) { + trash_path = gf_strdup (brickinfo->path); + strcat(trash_path, "/"); + strcat(trash_path, value); + + /* Checks whether a directory with + given option exists or not */ + if (!stat(trash_path, &stbuf)) { + snprintf (errstr, sizeof (errstr), + "Path %s exists", value); + gf_log (this->name, GF_LOG_ERROR, + "Unable to set the options in " + "'volume set': %s", errstr); + ret = -1; + goto out; + } else { + gf_log (this->name, GF_LOG_DEBUG, + "Directory with given name " + "does not exists, continuing"); + } + + if (volinfo->status == GLUSTERD_STATUS_STARTED + && brickinfo->status != GF_BRICK_STARTED) { + /* If volume is in started state , checks + whether bricks are online */ + snprintf (errstr, sizeof (errstr), + "One or more bricks are down"); + gf_log (this->name, GF_LOG_ERROR, + "Unable to set the options in " + "'volume set': %s", errstr); + ret = -1; + goto out; + } + } + } + } else if (!strcmp(key, "features.trash-dir") && !trash_enabled) { + snprintf (errstr, sizeof (errstr), + "Trash translator is not enabled. Use " + "volume set %s trash on", volname); + gf_log (this->name, GF_LOG_ERROR, + "Unable to set the options in 'volume " + "set': %s", errstr); + ret = -1; + goto out; + } ret = dict_set_str (val_dict, key, value); if (ret) { @@ -1015,6 +1090,9 @@ out: if (val_dict) dict_unref (val_dict); + if (trash_path) + GF_FREE (trash_path); + GF_FREE (key_fixed); if (errstr[0] != '\0') *op_errstr = gf_strdup (errstr); diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c index 1af7a77cff1..779d6be34a4 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.c +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c @@ -1421,6 +1421,7 @@ brick_graph_add_posix (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, { int ret = -1; gf_boolean_t quota_enabled = _gf_true; + gf_boolean_t trash_enabled = _gf_false; gf_boolean_t pgfid_feat = _gf_false; char *value = NULL; xlator_t *xl = NULL; @@ -1435,6 +1436,13 @@ brick_graph_add_posix (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, goto out; } + ret = glusterd_volinfo_get (volinfo, VKEY_FEATURES_TRASH, &value); + if (value) { + ret = gf_string2boolean (value, &trash_enabled); + if (ret) + goto out; + } + ret = glusterd_volinfo_get (volinfo, "update-link-count-parent", &value); @@ -1459,7 +1467,7 @@ brick_graph_add_posix (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, if (ret) goto out; - if (quota_enabled || pgfid_feat) + if (quota_enabled || pgfid_feat || trash_enabled) xlator_set_option (xl, "update-link-count-parent", "on"); out: @@ -1467,6 +1475,29 @@ out: } static int +brick_graph_add_trash (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, + dict_t *set_dict, glusterd_brickinfo_t *brickinfo) +{ + int ret = -1; + xlator_t *xl = NULL; + + xl = volgen_graph_add (graph, "features/trash", volinfo->volname); + if (!xl) + goto out; + ret = xlator_set_option (xl, "trash-dir", ".trashcan"); + if (ret) + goto out; + ret = xlator_set_option (xl, "brick-path", brickinfo->path); + if (ret) + goto out; + ret = xlator_set_option (xl, "trash-internal-op", "off"); + if (ret) + goto out; +out: + return ret; +} + +static int brick_graph_add_bd (volgen_graph_t *graph, glusterd_volinfo_t *volinfo, dict_t *set_dict, glusterd_brickinfo_t *brickinfo) { @@ -2018,6 +2049,7 @@ static volgen_brick_xlator_t server_graph_table[] = { {brick_graph_add_acl, "acl"}, {brick_graph_add_changelog, "changelog"}, {brick_graph_add_bd, "bd"}, + {brick_graph_add_trash, "trash"}, {brick_graph_add_posix, "posix"}, }; diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.h b/xlators/mgmt/glusterd/src/glusterd-volgen.h index bc9c2265384..947de76c926 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volgen.h +++ b/xlators/mgmt/glusterd/src/glusterd-volgen.h @@ -32,6 +32,7 @@ #define VKEY_MARKER_XTIME_FORCE GEOREP".ignore-pid-check" #define VKEY_CHANGELOG "changelog.changelog" #define VKEY_FEATURES_QUOTA "features.quota" +#define VKEY_FEATURES_TRASH "features.trash" #define AUTH_ALLOW_MAP_KEY "auth.allow" #define AUTH_REJECT_MAP_KEY "auth.reject" diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-set.c b/xlators/mgmt/glusterd/src/glusterd-volume-set.c index b0cf2609210..77f6853dd51 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c +++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c @@ -1641,6 +1641,28 @@ struct volopt_map_entry glusterd_volopt_map[] = { .voltype = "mgmt/glusterd", .op_version = GD_OP_VERSION_3_6_0, }, + + /*Trash translator options */ + { .key = "features.trash", + .voltype = "features/trash", + .op_version = GD_OP_VERSION_3_7_0, + }, + { .key = "features.trash-dir", + .voltype = "features/trash", + .op_version = GD_OP_VERSION_3_7_0, + }, + { .key = "features.trash-eliminate-path", + .voltype = "features/trash", + .op_version = GD_OP_VERSION_3_7_0, + }, + { .key = "features.trash-max-filesize", + .voltype = "features/trash", + .op_version = GD_OP_VERSION_3_7_0, + }, + { .key = "features.trash-internal-op", + .voltype = "features/trash", + .op_version = GD_OP_VERSION_3_7_0, + }, { .key = "locks.trace", .voltype = "features/locks", .type = NO_DOC, |