summaryrefslogtreecommitdiffstats
path: root/xlators/features/read-only/src/worm.c
diff options
context:
space:
mode:
authorkarthik-us <ksubrahm@redhat.com>2016-02-11 16:31:18 +0530
committerNiels de Vos <ndevos@redhat.com>2016-05-01 18:05:22 -0700
commita15195794c336ed0e272076a128c56b171cae12f (patch)
tree3d613809d5b72c5483cca26919c7d2a2b9b67cb7 /xlators/features/read-only/src/worm.c
parentc804ac76c404acb7277cfb9c0a7159bc33d92ff5 (diff)
WORM/Retention Translator: Implementation of file level WORM
To activate the file level worm feature, the features.read-only and features.worm options should be switched "off" on the volume and the features.worm-file-level should be switched "on". Both read-only and worm or worm-file-level cannot be switched "on" together. The files which are created when the worm-file-level option is set on the volume will have their own retention profile. If both worm and worm-file-level are "on" at that time the worm which is the volume level worm will have priority over file level worm. If worm-file level is switched "off" after some time and the read-only option is switched "on" then read-only will have priority. The current implementation allows the users to manually transmit a file to a WORM-Retained state by removing all the write bits of the file using the chmod command. The file will have a retention profile which contains the state of the file, mode of retention, and the default retention time. The file will be made WORM-Retained for a default of 120 seconds during which it will be immutable and undeletable and it sets the atime of the file to the time till which it is retained. After that period if any fop request comes for that file, will make the transition from WORM-Retained state to WORM state, where the file will be immutable but deletable and, it will reset the atime to the actual atime of the file. If a WORM file needs to be made undeletable again, it can be done by using the chmod command with all the write bits removed. There are two modes of retention: 1. Relax: where the retention time of a WORM-Retained file can be increased or decreased. 2. Enterprise: where the retention time of a WORM-Retained file can be increased but not be decreased. Whenever a utime change(touch -a, -t, ...)request comes for a file it checks the mode of retention before setting the utimes. This is done only if the file is WORM-Retained but for a WORM file it will change the utimes. Lazy auto commit: Whenever a file gets created it will store the creation time of the file or if a file already exists then any of the next unlink, link, truncate or rename fops will set the current time as the start time in an xattr. The next rename/unlink/truncate/link call will check for the auto commit period and if is is expired, then it will automatically do the state transition. If it is a normal file then it gets converted to WORM-Retained state. If it is a WORM-Retained file and its retention period is expired, then it gets converted to WORM state. Added the volume set options for the WORM translator. It allows the users to change the default values of auto-commit-period, default-retention-period, retention-mode. To make use of the file-level WORM first we have to set the 'worm-file' option to 'on'. The files which are created when the worm-file option is set on the volume will get WORM-Retained. Other files will work as usual and will not be WORMed. The auto-commit-period, retention-mode, and the default-retention-period values for the file will be set to the values which are set on the volume when the file is created. Added the tests to check the basic functionalities of the WORM/Retention feature. Change-Id: I77bd9777f9395a944d76b5cc35a5b48a3c14d148 BUG: 1326308 Signed-off-by: karthik-us <ksubrahm@redhat.com> Reviewed-on: http://review.gluster.org/13429 Reviewed-by: Niels de Vos <ndevos@redhat.com> Smoke: Gluster Build System <jenkins@build.gluster.com> NetBSD-regression: NetBSD Build System <jenkins@build.gluster.org> CentOS-regression: Gluster Build System <jenkins@build.gluster.com>
Diffstat (limited to 'xlators/features/read-only/src/worm.c')
-rw-r--r--xlators/features/read-only/src/worm.c483
1 files changed, 454 insertions, 29 deletions
diff --git a/xlators/features/read-only/src/worm.c b/xlators/features/read-only/src/worm.c
index f117e206285..2f35f1fc9f1 100644
--- a/xlators/features/read-only/src/worm.c
+++ b/xlators/features/read-only/src/worm.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
+ Copyright (c) 2008-2012, 2016 Red Hat, Inc. <http://www.redhat.com>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
@@ -12,6 +12,9 @@
#include "read-only-common.h"
#include "read-only-mem-types.h"
#include "read-only.h"
+#include "syncop.h"
+#include "worm-helper.h"
+
int32_t
mem_acct_init (xlator_t *this)
@@ -26,35 +29,393 @@ mem_acct_init (xlator_t *this)
return ret;
}
-static int32_t
-worm_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret,
- int32_t op_errno, fd_t *fd, dict_t *xdata)
-{
- STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd, xdata);
- return 0;
-}
int32_t
worm_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
fd_t *fd, dict_t *xdata)
{
if (is_readonly_or_worm_enabled (this) &&
- ((((flags & O_ACCMODE) == O_WRONLY) ||
- ((flags & O_ACCMODE) == O_RDWR)) &&
- !(flags & O_APPEND))) {
+ (flags & (O_WRONLY | O_RDWR | O_APPEND))) {
STACK_UNWIND_STRICT (open, frame, -1, EROFS, NULL, NULL);
return 0;
}
- STACK_WIND (frame, worm_open_cbk, FIRST_CHILD(this),
+ STACK_WIND_TAIL (frame, FIRST_CHILD(this),
FIRST_CHILD(this)->fops->open, loc, flags, fd, xdata);
return 0;
}
+
+int32_t
+worm_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
+ dict_t *xdata)
+{
+ int ret = -1;
+ int label = -1;
+
+ if (is_readonly_or_worm_enabled (this))
+ goto unwind;
+ gf_uuid_copy (oldloc->gfid, oldloc->inode->gfid);
+ if (is_wormfile (this, _gf_false, oldloc))
+ goto wind;
+ label = state_transition (this, _gf_false, oldloc, GF_FOP_LINK, &ret);
+ if (label == 0)
+ goto wind;
+ if (label == 1)
+ goto unwind;
+ if (label == 2)
+ goto out;
+
+unwind:
+ STACK_UNWIND_STRICT (link, frame, -1, EROFS, NULL, NULL, NULL, NULL,
+ NULL);
+ ret = 0;
+ goto out;
+wind:
+ STACK_WIND_TAIL (frame, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->link,
+ oldloc, newloc, xdata);
+ ret = 0;
+out:
+ if (label == 2)
+ STACK_UNWIND_STRICT (link, frame, -1, ret, NULL, NULL,
+ NULL, NULL, NULL);
+ return ret;
+}
+
+
+int32_t
+worm_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ dict_t *xdata)
+{
+ int ret = -1;
+ int label = -1;
+
+ if (is_readonly_or_worm_enabled (this))
+ goto unwind;
+ gf_uuid_copy (loc->gfid, loc->inode->gfid);
+ if (is_wormfile (this, _gf_false, loc))
+ goto wind;
+ label = state_transition (this, _gf_false, loc, GF_FOP_UNLINK, &ret);
+ if (label == 0)
+ goto wind;
+ if (label == 1)
+ goto unwind;
+ if (label == 2)
+ goto out;
+
+unwind:
+ STACK_UNWIND_STRICT (unlink, frame, -1, EROFS, NULL, NULL, NULL);
+ ret = 0;
+ goto out;
+wind:
+ STACK_WIND_TAIL (frame, FIRST_CHILD(this),
+ FIRST_CHILD(this)->fops->unlink,
+ loc, flags, xdata);
+ ret = 0;
+out:
+ if (label == 2)
+ STACK_UNWIND_STRICT (unlink, frame, -1, ret,
+ NULL, NULL, NULL);
+ return ret;
+}
+
+
+int32_t
+worm_rename (call_frame_t *frame, xlator_t *this,
+ loc_t *oldloc, loc_t *newloc, dict_t *xdata)
+{
+ int ret = -1;
+ int label = -1;
+
+ if (is_readonly_or_worm_enabled (this))
+ goto unwind;
+ gf_uuid_copy (oldloc->gfid, oldloc->inode->gfid);
+ if (is_wormfile (this, _gf_false, oldloc))
+ goto wind;
+ label = state_transition (this, _gf_false, oldloc, GF_FOP_RENAME,
+ &ret);
+ if (label == 0)
+ goto wind;
+ if (label == 1)
+ goto unwind;
+ if (label == 2)
+ goto out;
+
+unwind:
+ STACK_UNWIND_STRICT (rename, frame, -1, EROFS, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+ ret = 0;
+ goto out;
+wind:
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->rename,
+ oldloc, newloc, xdata);
+ ret = 0;
+out:
+ if (label == 2)
+ STACK_UNWIND_STRICT (rename, frame, -1, ret, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+ return ret;
+}
+
+
+int32_t
+worm_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset,
+ dict_t *xdata)
+{
+ int ret = -1;
+ int label = -1;
+
+ if (is_readonly_or_worm_enabled (this))
+ goto unwind;
+ if (is_wormfile (this, _gf_false, loc))
+ goto wind;
+ label = state_transition (this, _gf_false, loc, GF_FOP_TRUNCATE,
+ &ret);
+ if (label == 0)
+ goto wind;
+ if (label == 1)
+ goto unwind;
+ if (label == 2)
+ goto out;
+
+unwind:
+ STACK_UNWIND_STRICT (truncate, frame, -1, EROFS,
+ NULL, NULL, NULL);
+ ret = 0;
+ goto out;
+
+wind:
+ STACK_WIND_TAIL (frame,
+ FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->truncate,
+ loc, offset, xdata);
+ ret = 0;
+out:
+ if (label == 2)
+ STACK_UNWIND_STRICT (truncate, frame, -1, ret,
+ NULL, NULL, NULL);
+ return ret;
+}
+
+
+int32_t
+worm_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ struct iatt *stbuf, int32_t valid, dict_t *xdata)
+{
+ gf_boolean_t rd_only = _gf_false;
+ worm_reten_state_t reten_state = {0,};
+ struct iatt stpre = {0,};
+ int ret = -1;
+
+ if (is_wormfile (this, _gf_false, loc))
+ goto wind;
+ if (valid & GF_SET_ATTR_MODE) {
+ rd_only = is_write_disabled (stbuf);
+ if (!rd_only)
+ goto wind;
+
+ ret = worm_set_state (this, _gf_false, loc,
+ &reten_state, stbuf);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Error setting worm state");
+ goto out;
+ }
+ } else if (valid & GF_SET_ATTR_ATIME) {
+ ret = worm_get_state (this, _gf_false, loc, &reten_state);
+ if (ret)
+ goto wind;
+ if (reten_state.retain) {
+ ret = syncop_stat (this, loc, &stpre, NULL, NULL);
+ if (ret)
+ goto out;
+ if (reten_state.ret_mode == 0) {
+ if (stbuf->ia_atime < stpre.ia_mtime) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Cannot set atime less than "
+ "the mtime for a WORM-Retained "
+ "file");
+ goto unwind;
+ }
+ } else {
+ if (stbuf->ia_atime < stpre.ia_atime) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Cannot decrease the atime of a"
+ " WORM-Retained file in "
+ "Enterprise mode");
+ goto unwind;
+ }
+ }
+ stbuf->ia_mtime = stpre.ia_mtime;
+ }
+ }
+
+wind:
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->setattr,
+ loc, stbuf, valid, xdata);
+ ret = 0;
+ goto out;
+unwind:
+ STACK_UNWIND_STRICT (setattr, frame, -1, EROFS, NULL, NULL, NULL);
+out:
+ return ret;
+}
+
+
+int32_t
+worm_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iatt *stbuf, int32_t valid, dict_t *xdata)
+{
+ gf_boolean_t rd_only = _gf_false;
+ worm_reten_state_t reten_state = {0,};
+ struct iatt stpre = {0,};
+ int ret = -1;
+
+ if (is_wormfile (this, _gf_true, fd))
+ goto wind;
+ if (valid & GF_SET_ATTR_MODE) {
+ rd_only = is_write_disabled (stbuf);
+ if (!rd_only)
+ goto wind;
+
+ ret = worm_set_state (this, _gf_true, fd,
+ &reten_state, stbuf);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Error setting worm state");
+ goto out;
+ }
+ } else if (valid & GF_SET_ATTR_ATIME) {
+ ret = worm_get_state (this, _gf_true, fd, &reten_state);
+ if (ret)
+ goto wind;
+ if (reten_state.retain) {
+ ret = syncop_fstat (this, fd, &stpre, NULL, NULL);
+ if (ret)
+ goto out;
+ if (reten_state.ret_mode == 0) {
+ if (stbuf->ia_atime < stpre.ia_mtime) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Cannot set atime less than "
+ "the mtime for a WORM-Retained "
+ "file");
+ goto unwind;
+ }
+ } else {
+ if (stbuf->ia_atime < stpre.ia_atime) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Cannot decrease the atime of a"
+ " WORM-Retained file in "
+ "Enterprise mode");
+ goto unwind;
+ }
+ }
+ stbuf->ia_mtime = stpre.ia_mtime;
+ }
+ }
+
+wind:
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->fsetattr,
+ fd, stbuf, valid, xdata);
+ ret = 0;
+ goto out;
+unwind:
+ STACK_UNWIND_STRICT (fsetattr, frame, -1, EROFS, NULL, NULL, NULL);
+out:
+ return ret;
+}
+
+
+int32_t
+worm_writev (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ struct iovec *vector, int32_t count, off_t offset, uint32_t flags,
+ struct iobref *iobref, dict_t *xdata)
+{
+ worm_reten_state_t reten_state = {0,};
+ int ret = -1;
+
+ if (is_readonly_or_worm_enabled (this))
+ goto unwind;
+ if (is_wormfile (this, _gf_true, fd))
+ goto wind;
+ ret = worm_get_state (this, _gf_true, fd, &reten_state);
+ if (!reten_state.worm)
+ goto wind;
+
+unwind:
+ STACK_UNWIND_STRICT (writev, frame, -1, EROFS, NULL, NULL, NULL);
+ goto out;
+
+wind:
+ STACK_WIND_TAIL (frame,
+ FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->writev,
+ fd, vector, count, offset, flags,
+ iobref, xdata);
+ gf_log (this->name, GF_LOG_INFO, "WORM writev");
+ ret = 0;
+
+out:
+ return ret;
+}
+
+
+int32_t
+worm_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata)
+{
+ int ret = -1;
+ read_only_priv_t *priv = NULL;
+ dict_t *dict = NULL;
+
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD(this)->fops->create, loc, flags,
+ mode, umask, fd, xdata);
+ priv = this->private;
+ GF_ASSERT (priv);
+
+ if (priv->worm_file) {
+ dict = dict_new ();
+ if (!dict) {
+ gf_log (this->name, GF_LOG_ERROR, "Error creating the "
+ "dict");
+ goto out;
+ }
+ GF_VALIDATE_OR_GOTO (this->name, dict, out);
+ ret = dict_set_int8 (dict, "trusted.worm_file", 1);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "Error in setting "
+ "the dict");
+ goto out;
+ }
+ ret = syncop_fsetxattr (this, fd, dict, 0, NULL, NULL);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Error setting xattr");
+ goto out;
+ }
+ ret = worm_init_state (this, _gf_true, fd);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "Error initializing state");
+ }
+ }
+
+out:
+ if (dict)
+ dict_destroy (dict);
+ return ret;
+}
+
+
int32_t
init (xlator_t *this)
{
- int ret = -1;
+ int ret = -1;
read_only_priv_t *priv = NULL;
if (!this->children || this->children->next) {
@@ -68,11 +429,34 @@ init (xlator_t *this)
"dangling volume. check volfile ");
}
- priv = GF_CALLOC (1, sizeof (*priv), gf_read_only_mt_priv_t);
- if (!priv)
+ this->local_pool = mem_pool_new (read_only_priv_t, 64);
+ if (!this->local_pool) {
+ ret = -1;
+ gf_log (this->name, GF_LOG_ERROR,
+ "failed to create read_only_priv_t's memory pool");
+ goto out;
+ }
+
+ priv = mem_get0 (this->local_pool);
+ if (!priv) {
+ gf_log (this->name, GF_LOG_ERROR, "Error allocating priv");
+ goto out;
+ }
+
+ priv->reten_mode = mem_get0 (this->local_pool);
+ if (!priv->reten_mode) {
+ gf_log (this->name, GF_LOG_ERROR, "Error allocating "
+ "reten_mode");
goto out;
+ }
- GF_OPTION_INIT ("worm", priv->readonly_or_worm_enabled, bool, out);
+ GF_OPTION_INIT ("worm", priv->readonly_or_worm_enabled,
+ bool, out);
+ GF_OPTION_INIT ("worm-file-level", priv->worm_file, bool, out);
+ GF_OPTION_INIT ("default-retention-period", priv->reten_period,
+ uint64, out);
+ GF_OPTION_INIT ("auto-commit-period", priv->com_period, uint64, out);
+ GF_OPTION_INIT ("retention-mode", priv->reten_mode, str, out);
this->private = priv;
ret = 0;
@@ -80,25 +464,33 @@ out:
return ret;
}
+
int
reconfigure (xlator_t *this, dict_t *options)
{
- read_only_priv_t *priv = NULL;
+ read_only_priv_t *priv = NULL;
int ret = -1;
- gf_boolean_t readonly_or_worm_enabled = _gf_false;
priv = this->private;
GF_ASSERT (priv);
- GF_OPTION_RECONF ("worm", readonly_or_worm_enabled, options, bool, out);
-
- priv->readonly_or_worm_enabled = readonly_or_worm_enabled;
+ GF_OPTION_RECONF ("worm", priv->readonly_or_worm_enabled,
+ options, bool, out);
+ GF_OPTION_RECONF ("worm-file-level", priv->worm_file, options, bool,
+ out);
+ GF_OPTION_RECONF ("default-retention-period", priv->reten_period,
+ options, uint64, out);
+ GF_OPTION_RECONF ("retention-mode", priv->reten_mode, options, str,
+ out);
+ GF_OPTION_RECONF ("auto-commit-period", priv->com_period, options,
+ uint64, out);
ret = 0;
out:
gf_log (this->name, GF_LOG_DEBUG, "returning %d", ret);
return ret;
}
+
void
fini (xlator_t *this)
{
@@ -106,21 +498,31 @@ fini (xlator_t *this)
priv = this->private;
if (!priv)
- return;
-
+ goto out;
+ if (priv->reten_mode != NULL) {
+ mem_put (priv->reten_mode);
+ priv->reten_mode = NULL;
+ }
+ mem_put (priv);
this->private = NULL;
- GF_FREE (priv);
-
+ mem_pool_destroy (this->local_pool);
+out:
return;
}
+
struct xlator_fops fops = {
.open = worm_open,
+ .writev = worm_writev,
+ .setattr = worm_setattr,
+ .fsetattr = worm_fsetattr,
+ .rename = worm_rename,
+ .link = worm_link,
+ .unlink = worm_unlink,
+ .truncate = worm_truncate,
+ .create = worm_create,
- .unlink = ro_unlink,
.rmdir = ro_rmdir,
- .rename = ro_rename,
- .truncate = ro_truncate,
.removexattr = ro_removexattr,
.fsyncdir = ro_fsyncdir,
.xattrop = ro_xattrop,
@@ -131,8 +533,10 @@ struct xlator_fops fops = {
.lk = ro_lk,
};
+
struct xlator_cbks cbks;
+
struct volume_options options[] = {
{ .key = {"worm"},
.type = GF_OPTION_TYPE_BOOL,
@@ -140,5 +544,26 @@ struct volume_options options[] = {
.description = "When \"on\", makes a volume get write once read many "
" feature. It is turned \"off\" by default."
},
+ { .key = {"worm-file-level"},
+ .type = GF_OPTION_TYPE_BOOL,
+ .default_value = "off",
+ .description = "When \"on\", activates the file level worm. "
+ "It is turned \"off\" by default."
+ },
+ { .key = {"default-retention-period"},
+ .type = GF_OPTION_TYPE_TIME,
+ .default_value = "120",
+ .description = "The default retention period for the files."
+ },
+ { .key = {"retention-mode"},
+ .type = GF_OPTION_TYPE_STR,
+ .default_value = "relax",
+ .description = "The mode of retention (relax/enterprise). "
+ "It is relax by default."
+ },
+ { .key = {"auto-commit-period"},
+ .type = GF_OPTION_TYPE_TIME,
+ .default_value = "180",
+ .description = "Auto commit period for the files."
+ },
};
-