summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xtests/features/worm.t54
-rw-r--r--xlators/features/read-only/src/Makefile.am4
-rw-r--r--xlators/features/read-only/src/read-only.h17
-rw-r--r--xlators/features/read-only/src/worm-helper.c422
-rw-r--r--xlators/features/read-only/src/worm-helper.h36
-rw-r--r--xlators/features/read-only/src/worm.c483
-rw-r--r--xlators/mgmt/glusterd/src/glusterd-volgen.c10
-rw-r--r--xlators/mgmt/glusterd/src/glusterd-volume-set.c113
8 files changed, 1098 insertions, 41 deletions
diff --git a/tests/features/worm.t b/tests/features/worm.t
new file mode 100755
index 0000000..407b49a
--- /dev/null
+++ b/tests/features/worm.t
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+. $(dirname $0)/../include.rc
+. $(dirname $0)/../volume.rc
+
+cleanup;
+
+TEST glusterd
+TEST pidof glusterd
+
+TEST $CLI volume create $V0 $H0:$B0/${V0}1
+
+EXPECT "$V0" volinfo_field $V0 'Volume Name'
+EXPECT 'Created' volinfo_field $V0 'Status'
+
+TEST $CLI volume start $V0
+EXPECT 'Started' volinfo_field $V0 'Status'
+
+## Mount FUSE with caching disabled (read-write)
+TEST $GFS -s $H0 --volfile-id $V0 $M0
+
+## Tests for the volume level WORM
+TEST `echo "File 1" > $M0/file1`
+TEST touch $M0/file2
+
+## Enable the volume level WORM
+TEST $CLI volume set $V0 features.worm 1
+TEST ! mv $M0/file1 $M0/file11
+TEST `echo "block" > $M0/file2`
+
+## Disable the volume level WORM and delete the legacy files
+TEST $CLI volume set $V0 features.worm 0
+TEST rm -f $M0/*
+
+## Enable file level WORM
+TEST $CLI volume set $V0 features.worm-file-level 1
+TEST $CLI volume set $V0 features.default-retention-period 10
+TEST $CLI volume set $V0 features.auto-commit-period 5
+
+## Tests for manual transition to WORM/Retained state
+TEST `echo "worm 1" > $M0/file1`
+TEST chmod 0444 $M0/file1
+sleep 5
+TEST `echo "line 1" > $M0/file1`
+TEST ! mv $M0/file1 $M0/file2
+sleep 10
+TEST ! link $M0/file1 $M0/file2
+sleep 5
+TEST rm -f $M0/file1
+
+TEST $CLI volume stop $V0
+EXPECT 'Stopped' volinfo_field $V0 'Status'
+
+cleanup;
diff --git a/xlators/features/read-only/src/Makefile.am b/xlators/features/read-only/src/Makefile.am
index 5aad707..3edac3f 100644
--- a/xlators/features/read-only/src/Makefile.am
+++ b/xlators/features/read-only/src/Makefile.am
@@ -2,7 +2,7 @@ xlator_LTLIBRARIES = read-only.la worm.la
xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features
-noinst_HEADERS = read-only.h read-only-mem-types.h read-only-common.h
+noinst_HEADERS = read-only.h read-only-mem-types.h read-only-common.h worm-helper.h
read_only_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS)
@@ -11,7 +11,7 @@ read_only_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la
worm_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS)
-worm_la_SOURCES = read-only-common.c worm.c
+worm_la_SOURCES = read-only-common.c worm-helper.c worm.c
worm_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la
AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src
diff --git a/xlators/features/read-only/src/read-only.h b/xlators/features/read-only/src/read-only.h
index 8e7e1b6..3178bb2 100644
--- a/xlators/features/read-only/src/read-only.h
+++ b/xlators/features/read-only/src/read-only.h
@@ -15,7 +15,22 @@
#include "xlator.h"
typedef struct {
- gf_boolean_t readonly_or_worm_enabled;
+ uint8_t worm : 1;
+ uint8_t retain : 1;
+ uint8_t legal_hold :1;
+ uint8_t ret_mode : 1;
+ uint64_t ret_period;
+ uint64_t auto_commit_period;
+} worm_reten_state_t;
+
+
+typedef struct {
+ gf_boolean_t readonly_or_worm_enabled;
+ gf_boolean_t worm_file;
+ uint64_t reten_period;
+ uint64_t com_period;
+ char *reten_mode;
+ time_t start_time;
} read_only_priv_t;
#endif
diff --git a/xlators/features/read-only/src/worm-helper.c b/xlators/features/read-only/src/worm-helper.c
new file mode 100644
index 0000000..b5b1c62
--- /dev/null
+++ b/xlators/features/read-only/src/worm-helper.c
@@ -0,0 +1,422 @@
+/*
+ Copyright (c) 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
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+#include "read-only-mem-types.h"
+#include "read-only.h"
+#include "xlator.h"
+#include "syncop.h"
+#include "worm-helper.h"
+
+/*Function to check whether file is read-only.
+ * The input *stbuf contains the attributes of the file, which is used to check
+ * the write protection bits for all the users of the file.
+ * Return true if all the write bits are disabled,false otherwise*/
+gf_boolean_t
+is_write_disabled (struct iatt *stbuf)
+{
+ gf_boolean_t ret = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("worm", stbuf, out);
+
+ if (stbuf->ia_prot.owner.write == 0 &&
+ stbuf->ia_prot.group.write == 0 &&
+ stbuf->ia_prot.other.write == 0)
+ ret = _gf_true;
+out:
+ return ret;
+}
+
+
+int32_t
+worm_init_state (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr)
+{
+ int ret = -1;
+ uint64_t start_time = 0;
+ dict_t *dict = NULL;
+
+ GF_VALIDATE_OR_GOTO ("worm", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, file_ptr, out);
+
+ start_time = time (NULL);
+ dict = dict_new ();
+ if (!dict) {
+ gf_log (this->name, GF_LOG_ERROR, "Error creating the dict");
+ goto out;
+ }
+ ret = dict_set_uint64 (dict, "trusted.start_time", start_time);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "Error in setting the dict");
+ goto out;
+ }
+ if (fop_with_fd)
+ ret = syncop_fsetxattr (this, (fd_t *)file_ptr, dict, 0,
+ NULL, NULL);
+ else
+ ret = syncop_setxattr (this, (loc_t *)file_ptr, dict, 0, NULL,
+ NULL);
+out:
+ if (dict)
+ dict_destroy (dict);
+ return ret;
+}
+
+
+/*Function to set the retention state for a file.
+ * It loads the WORM/Retention state into the retention_state pointer.*/
+int32_t
+worm_set_state (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr,
+ worm_reten_state_t *retention_state, struct iatt *stbuf)
+{
+ read_only_priv_t *priv = NULL;
+ struct iatt stpre = {0,};
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("worm", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, file_ptr, out);
+ GF_VALIDATE_OR_GOTO (this->name, retention_state, out);
+ GF_VALIDATE_OR_GOTO (this->name, stbuf, out);
+
+ priv = this->private;
+ GF_ASSERT (priv);
+ retention_state->worm = 1;
+ retention_state->retain = 1;
+ retention_state->legal_hold = 0;
+ if (strcmp (priv->reten_mode, "relax") == 0)
+ retention_state->ret_mode = 0;
+ else
+ retention_state->ret_mode = 1;
+ retention_state->ret_period = priv->reten_period;
+ retention_state->auto_commit_period = priv->com_period;
+ if (fop_with_fd)
+ ret = syncop_fstat (this, (fd_t *)file_ptr, &stpre, NULL, NULL);
+ else
+ ret = syncop_stat (this, (loc_t *)file_ptr, &stpre, NULL, NULL);
+ if (ret)
+ goto out;
+ stbuf->ia_mtime = stpre.ia_mtime;
+ stbuf->ia_atime = time (NULL) + retention_state->ret_period;
+
+ if (fop_with_fd)
+ ret = syncop_fsetattr (this, (fd_t *)file_ptr, stbuf,
+ GF_SET_ATTR_ATIME, NULL, NULL,
+ NULL, NULL);
+ else
+ ret = syncop_setattr (this, (loc_t *)file_ptr, stbuf,
+ GF_SET_ATTR_ATIME, NULL, NULL,
+ NULL, NULL);
+ if (ret)
+ goto out;
+
+ ret = set_xattr (this, retention_state, fop_with_fd, file_ptr);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "Error setting xattr");
+ goto out;
+ }
+ ret = 0;
+out:
+ return ret;
+}
+
+
+/*This function gets the state of the WORM/Retention xattr and loads it in the
+ * dict pointer.*/
+int32_t
+worm_get_state (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr,
+ worm_reten_state_t *reten_state)
+{
+ dict_t *dict = NULL;
+ char *val = NULL;
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("worm", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, file_ptr, out);
+ GF_VALIDATE_OR_GOTO (this->name, reten_state, out);
+
+ if (fop_with_fd)
+ ret = syncop_fgetxattr (this, (fd_t *)file_ptr, &dict,
+ "trusted.reten_state", NULL, NULL);
+ else
+ ret = syncop_getxattr (this, (loc_t *)file_ptr, &dict,
+ "trusted.reten_state", NULL, NULL);
+ if (ret < 0 || !dict) {
+ ret = -1;
+ goto out;
+ }
+ ret = dict_get_str (dict, "trusted.reten_state", &val);
+ if (ret) {
+ ret = -2;
+ gf_log (this->name, GF_LOG_ERROR, "Empty val");
+ }
+ deserialize_state (val, reten_state);
+out:
+ if (dict)
+ dict_unref (dict);
+ return ret;
+}
+
+
+/*Function to lookup the current state of the WORM/Retention profile.
+ * Based on the retain value and the access time of the file, the transition
+ * from WORM/Retention to WORM is made.*/
+void
+state_lookup (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr,
+ worm_reten_state_t *reten_state)
+{
+ int ret = -1;
+ struct iatt stbuf = {0,};
+
+ GF_VALIDATE_OR_GOTO ("worm", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, file_ptr, out);
+ GF_VALIDATE_OR_GOTO (this->name, reten_state, out);
+
+ if (fop_with_fd)
+ ret = syncop_fstat (this, (fd_t *)file_ptr, &stbuf, NULL, NULL);
+ else
+ ret = syncop_stat (this, (loc_t *)file_ptr, &stbuf, NULL, NULL);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "Stat lookup error: %s",
+ strerror (-ret));
+ goto out;
+ }
+ if (time (NULL) < stbuf.ia_atime)
+ goto out;
+
+ stbuf.ia_atime -= reten_state->ret_period;
+ reten_state->retain = 0;
+ reten_state->ret_period = 0;
+ reten_state->auto_commit_period = 0;
+ ret = set_xattr (this, reten_state, fop_with_fd, file_ptr);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "Error setting xattr");
+ goto out;
+ }
+
+ if (fop_with_fd)
+ ret = syncop_fsetattr (this, (fd_t *)file_ptr, &stbuf,
+ GF_SET_ATTR_ATIME, NULL, NULL,
+ NULL, NULL);
+ else
+ ret = syncop_setattr (this, (loc_t *)file_ptr, &stbuf,
+ GF_SET_ATTR_ATIME, NULL, NULL,
+ NULL, NULL);
+ if (ret)
+ goto out;
+ gf_log (this->name, GF_LOG_INFO, "Retention state reset");
+out:
+ return;
+}
+
+
+/*This function serializes and stores the WORM/Retention state of a file in an
+ * uint64_t variable by setting the bits using the bitwise operations.*/
+void
+serialize_state (worm_reten_state_t *reten_state, char *val)
+{
+ uint32_t state = 0;
+
+ GF_VALIDATE_OR_GOTO ("worm", reten_state, out);
+ GF_VALIDATE_OR_GOTO ("worm", val, out);
+
+ state |= reten_state->worm << 0;
+ state |= reten_state->retain << 1;
+ state |= reten_state->legal_hold << 2;
+ state |= reten_state->ret_mode << 3;
+ sprintf (val, "%d/%ld/%ld", state, reten_state->ret_period,
+ reten_state->auto_commit_period);
+
+out:
+ return;
+}
+
+
+/*This function deserializes the data stored in the xattr of the file and loads
+ * the value to the reten_state structure.*/
+void deserialize_state (char *val, worm_reten_state_t *reten_state)
+{
+ char *token = NULL;
+ uint32_t state = 0;
+
+ GF_VALIDATE_OR_GOTO ("worm", val, out);
+ GF_VALIDATE_OR_GOTO ("worm", reten_state, out);
+
+ token = strtok (val, "/");
+ state = atoi (token);
+ reten_state->worm = (state >> 0) & 1;
+ reten_state->retain = (state >> 1) & 1;
+ reten_state->legal_hold = (state >> 2) & 1;
+ reten_state->ret_mode = (state >> 3) & 1;
+ token = strtok (NULL, "/");
+ reten_state->ret_period = atoi (token);
+ token = strtok (NULL, "/");
+ reten_state->auto_commit_period = atoi (token);
+
+out:
+ return;
+}
+
+
+/*Function to set the xattr for a file.
+ * If the xattr is already present then it will replace that.*/
+int32_t
+set_xattr (xlator_t *this, worm_reten_state_t *reten_state,
+ gf_boolean_t fop_with_fd, void *file_ptr)
+{
+ char val[100] = "";
+ int ret = -1;
+ dict_t *dict = NULL;
+
+ GF_VALIDATE_OR_GOTO ("worm", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, reten_state, out);
+ GF_VALIDATE_OR_GOTO (this->name, file_ptr, out);
+
+ serialize_state (reten_state, val);
+ dict = dict_new ();
+ if (!dict) {
+ gf_log (this->name, GF_LOG_ERROR, "Error creating the dict");
+ goto out;
+ }
+ ret = dict_set_str (dict, "trusted.reten_state", val);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "Error in setting the dict");
+ goto out;
+ }
+ if (fop_with_fd)
+ ret = syncop_fsetxattr (this, (fd_t *)file_ptr, dict, 0,
+ NULL, NULL);
+ else
+ ret = syncop_setxattr (this, (loc_t *)file_ptr, dict, 0, NULL,
+ NULL);
+out:
+ if (dict)
+ dict_destroy (dict);
+ return ret;
+}
+
+
+/*This function checks whether a file's timeout is happend for the state
+ * transition and if yes, then it will do the transition from the current state
+ * to the appropriate state. It also decides whether to continue or to block
+ * the FOP.
+ * Return:
+ * 0 : If the FOP should continue i.e., if the file is not in the WORM-Retained
+ * state or if the FOP is unlink and the file is not in the Retained state.
+ * 1: If the FOP sholud block i.e., if the file is in WORM-Retained/WORM state.
+ * 2: Blocks the FOP if any operation fails while doing the state transition or
+ * fails to get the state of the file.*/
+int32_t
+state_transition (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr,
+ glusterfs_fop_t op, int *ret_val)
+{
+ int label = -1;
+ int ret = -1;
+ uint64_t com_period = 0;
+ uint64_t ret_period = 0;
+ uint64_t start_time = 0;
+ dict_t *dict = NULL;
+ worm_reten_state_t reten_state = {0,};
+ read_only_priv_t *priv = NULL;
+ struct iatt stbuf = {0,};
+
+ priv = this->private;
+ GF_ASSERT (priv);
+
+ if (fop_with_fd)
+ ret = syncop_fgetxattr (this, (fd_t *)file_ptr, &dict,
+ "trusted.start_time", NULL, NULL);
+ else
+ ret = syncop_getxattr (this, (loc_t *)file_ptr, &dict,
+ "trusted.start_time", NULL, NULL);
+ if (ret < 0 || !dict) {
+ ret = -2;
+ label = 2;
+ goto out;
+ }
+ ret = dict_get_uint64 (dict, "trusted.start_time", &start_time);
+ if (ret) {
+ label = 2;
+ goto out;
+ }
+
+ ret = worm_get_state (this, fop_with_fd, file_ptr, &reten_state);
+ if (ret == -2) {
+ ret = -1;
+ label = 2;
+ goto out;
+ }
+ com_period = priv->com_period;
+ if (ret == -1 && (time (NULL) - start_time) >= com_period) {
+ if (fop_with_fd)
+ ret = syncop_fstat (this, (fd_t *)file_ptr, &stbuf,
+ NULL, NULL);
+ else
+ ret = syncop_stat (this, (loc_t *)file_ptr, &stbuf,
+ NULL, NULL);
+ if (ret) {
+ label = 2;
+ goto out;
+ }
+ ret_period = priv->reten_period;
+ if ((time (NULL) - stbuf.ia_mtime) >= ret_period) {
+ ret = worm_set_state(this, fop_with_fd, file_ptr,
+ &reten_state, &stbuf);
+ if (ret) {
+ label = 2;
+ goto out;
+ }
+ label = 1;
+ goto out;
+ } else {
+ label = 0;
+ goto out;
+ }
+ } else if (ret == -1 && (time (NULL) - start_time)
+ < com_period) {
+ label = 0;
+ goto out;
+ } else if (reten_state.retain &&
+ (time (NULL) - start_time) >=
+ reten_state.auto_commit_period) {
+ state_lookup (this, fop_with_fd, file_ptr, &reten_state);
+ }
+ if (reten_state.retain)
+ label = 1;
+ else if (reten_state.worm && !reten_state.retain &&
+ op == GF_FOP_UNLINK)
+ label = 0;
+ else
+ label = 1;
+
+out:
+ if (dict)
+ dict_unref (dict);
+ *ret_val = ret;
+ return label;
+}
+
+
+/*Function to check whether a file is independently WORMed (i.e., file level
+ * WORM is set on the file). */
+int32_t
+is_wormfile (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr)
+{
+ int ret = -1;
+ dict_t *dict = NULL;
+
+ if (fop_with_fd)
+ ret = syncop_fgetxattr (this, (fd_t *)file_ptr, &dict,
+ "trusted.worm_file", NULL, NULL);
+ else
+ ret = syncop_getxattr (this, (loc_t *)file_ptr, &dict,
+ "trusted.worm_file", NULL, NULL);
+ if (dict) {
+ ret = 0;
+ dict_unref (dict);
+ }
+ return ret;
+} \ No newline at end of file
diff --git a/xlators/features/read-only/src/worm-helper.h b/xlators/features/read-only/src/worm-helper.h
new file mode 100644
index 0000000..a06a1d3
--- /dev/null
+++ b/xlators/features/read-only/src/worm-helper.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (c) 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
+ General Public License, version 3 or any later version (LGPLv3 or
+ later), or the GNU General Public License, version 2 (GPLv2), in all
+ cases as published by the Free Software Foundation.
+*/
+
+gf_boolean_t is_write_disabled (struct iatt *stbuf);
+
+int32_t worm_init_state (xlator_t *this, gf_boolean_t fop_with_fd,
+ void *file_ptr);
+
+int32_t worm_set_state (xlator_t *this, gf_boolean_t fop_with_fd,
+ void *file_ptr, worm_reten_state_t *retention_state,
+ struct iatt *stbuf);
+
+int32_t worm_get_state (xlator_t *this, gf_boolean_t fop_with_fd,
+ void *file_ptr, worm_reten_state_t *reten_state);
+
+void state_lookup (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr,
+ worm_reten_state_t *reten_state);
+
+void serialize_state (worm_reten_state_t *reten_state, char *val);
+
+void deserialize_state (char *val, worm_reten_state_t *reten_state);
+
+int32_t set_xattr (xlator_t *this, worm_reten_state_t *reten_state,
+ gf_boolean_t fop_with_fd, void *file_ptr);
+
+int32_t state_transition (xlator_t *this, gf_boolean_t fop_with_fd,
+ void *file_ptr, glusterfs_fop_t op, int *ret_val);
+
+int32_t is_wormfile (xlator_t *this, gf_boolean_t fop_with_fd, void *file_ptr);
diff --git a/xlators/features/read-only/src/worm.c b/xlators/features/read-only/src/worm.c
index f117e20..2f35f1f 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."
+ },
};
-
diff --git a/xlators/mgmt/glusterd/src/glusterd-volgen.c b/xlators/mgmt/glusterd/src/glusterd-volgen.c
index 2389023..e88116e 100644
--- a/xlators/mgmt/glusterd/src/glusterd-volgen.c
+++ b/xlators/mgmt/glusterd/src/glusterd-volgen.c
@@ -2075,7 +2075,8 @@ brick_graph_add_ro (volgen_graph_t *graph, glusterd_volinfo_t *volinfo,
goto out;
if (dict_get_str_boolean (set_dict, "features.read-only", 0) &&
- dict_get_str_boolean (set_dict, "features.worm", 0)) {
+ (dict_get_str_boolean (set_dict, "features.worm", 0) ||
+ dict_get_str_boolean (set_dict, "features.worm-file-level", 0))) {
gf_msg (THIS->name, GF_LOG_ERROR, errno,
GD_MSG_DICT_GET_FAILED,
"read-only and worm cannot be set together");
@@ -2107,7 +2108,8 @@ brick_graph_add_worm (volgen_graph_t *graph, glusterd_volinfo_t *volinfo,
goto out;
if (dict_get_str_boolean (set_dict, "features.read-only", 0) &&
- dict_get_str_boolean (set_dict, "features.worm", 0)) {
+ (dict_get_str_boolean (set_dict, "features.worm", 0) ||
+ dict_get_str_boolean (set_dict, "features.worm-file-level", 0))) {
gf_msg (THIS->name, GF_LOG_ERROR, 0,
GD_MSG_INCOMPATIBLE_VALUE,
"read-only and worm cannot be set together");
@@ -2402,8 +2404,6 @@ static volgen_brick_xlator_t server_graph_table[] = {
{brick_graph_add_server, NULL},
{brick_graph_add_io_stats, NULL},
{brick_graph_add_cdc, NULL},
- {brick_graph_add_ro, NULL},
- {brick_graph_add_worm, NULL},
{brick_graph_add_quota, "quota"},
{brick_graph_add_index, "index"},
{brick_graph_add_barrier, NULL},
@@ -2412,6 +2412,8 @@ static volgen_brick_xlator_t server_graph_table[] = {
{brick_graph_add_iot, "io-threads"},
{brick_graph_add_upcall, "upcall"},
{brick_graph_add_pump, NULL},
+ {brick_graph_add_ro, NULL},
+ {brick_graph_add_worm, NULL},
{brick_graph_add_locks, "locks"},
{brick_graph_add_acl, "acl"},
{brick_graph_add_bitrot_stub, "bitrot-stub"},
diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-set.c b/xlators/mgmt/glusterd/src/glusterd-volume-set.c
index 5bb9d90..68dec22 100644
--- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c
+++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c
@@ -863,6 +863,82 @@ out:
return ret;
}
+
+static int
+validate_worm (glusterd_volinfo_t *volinfo, dict_t *dict, char *key,
+ char *value, char **op_errstr)
+{
+ xlator_t *this = NULL;
+ gf_boolean_t b = _gf_false;
+ int ret = -1;
+
+ this = THIS;
+ GF_VALIDATE_OR_GOTO ("glusterd", this, out);
+ ret = gf_string2boolean (value, &b);
+ if (ret) {
+ gf_asprintf (op_errstr, "%s is not a valid boolean value. %s "
+ "expects a valid boolean value.", value, key);
+ gf_msg (this->name, GF_LOG_ERROR, 0,
+ GD_MSG_INVALID_ENTRY, "%s", *op_errstr);
+ }
+out:
+ gf_msg_debug ("glusterd", 0, "Returning %d", ret);
+
+ return ret;
+}
+
+
+static int
+validate_worm_period (glusterd_volinfo_t *volinfo, dict_t *dict, char *key,
+ char *value, char **op_errstr)
+{
+ xlator_t *this = NULL;
+ uint64_t period = -1;
+ int ret = -1;
+
+ this = THIS;
+ GF_VALIDATE_OR_GOTO ("glusterd", this, out);
+ ret = gf_string2uint64 (value, &period);
+ if (ret) {
+ gf_asprintf (op_errstr, "%s is not a valid uint64_t value."
+ " %s expects a valid uint64_t value.", value, key);
+ gf_msg (this->name, GF_LOG_ERROR, 0,
+ GD_MSG_INVALID_ENTRY, "%s", *op_errstr);
+ }
+out:
+ gf_msg_debug ("glusterd", 0, "Returning %d", ret);
+
+ return ret;
+}
+
+
+static int
+validate_reten_mode (glusterd_volinfo_t *volinfo, dict_t *dict, char *key,
+ char *value, char **op_errstr)
+{
+ xlator_t *this = NULL;
+ int ret = -1;
+
+ this = THIS;
+ GF_VALIDATE_OR_GOTO ("glusterd", this, out);
+ if ((strcmp (value, "relax") &&
+ strcmp (value, "enterprise"))) {
+ gf_asprintf (op_errstr, "The value of retention mode should be "
+ "either relax or enterprise. But the value"
+ " of %s is %s", key, value);
+ gf_msg (this->name, GF_LOG_ERROR, 0, GD_MSG_INVALID_ENTRY,
+ "%s", *op_errstr);
+ ret = -1;
+ goto out;
+ }
+ ret = 0;
+out:
+ gf_msg_debug ("glusterd", 0, "Returning %d", ret);
+
+ return ret;
+}
+
+
/* dispatch table for VOLUME SET
* -----------------------------
*
@@ -2314,13 +2390,40 @@ struct volopt_map_entry glusterd_volopt_map[] = {
.op_version = 1,
.flags = OPT_FLAG_CLIENT_OPT | OPT_FLAG_XLATOR_OPT
},
- { .key = "features.worm",
- .voltype = "features/worm",
- .option = "worm",
- .value = "off",
- .op_version = 2,
+ { .key = "features.worm",
+ .voltype = "features/worm",
+ .option = "worm",
+ .value = "off",
+ .validate_fn = validate_worm,
+ .op_version = 2,
+ .flags = OPT_FLAG_CLIENT_OPT | OPT_FLAG_XLATOR_OPT
+ },
+ { .key = "features.worm-file-level",
+ .voltype = "features/worm",
+ .option = "worm-file-level",
+ .value = "off",
+ .validate_fn = validate_worm,
+ .op_version = GD_OP_VERSION_3_8_0,
.flags = OPT_FLAG_CLIENT_OPT | OPT_FLAG_XLATOR_OPT
},
+ { .key = "features.default-retention-period",
+ .voltype = "features/worm",
+ .option = "default-retention-period",
+ .validate_fn = validate_worm_period,
+ .op_version = GD_OP_VERSION_3_8_0,
+ },
+ { .key = "features.retention-mode",
+ .voltype = "features/worm",
+ .option = "retention-mode",
+ .validate_fn = validate_reten_mode,
+ .op_version = GD_OP_VERSION_3_8_0,
+ },
+ { .key = "features.auto-commit-period",
+ .voltype = "features/worm",
+ .option = "auto-commit-period",
+ .validate_fn = validate_worm_period,
+ .op_version = GD_OP_VERSION_3_8_0,
+ },
{ .key = "storage.linux-aio",
.voltype = "storage/posix",
.op_version = 1