"Fossies" - the Fresh Open Source Software Archive

Member "glusterfs-8.2/xlators/features/read-only/src/worm.c" (16 Sep 2020, 21004 Bytes) of package /linux/misc/glusterfs-8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "worm.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2    Copyright (c) 2008-2012, 2016 Red Hat, Inc. <http://www.redhat.com>
    3    This file is part of GlusterFS.
    4 
    5    This file is licensed to you under your choice of the GNU Lesser
    6    General Public License, version 3 or any later version (LGPLv3 or
    7    later), or the GNU General Public License, version 2 (GPLv2), in all
    8    cases as published by the Free Software Foundation.
    9 */
   10 #include <glusterfs/xlator.h>
   11 #include <glusterfs/defaults.h>
   12 #include "read-only-common.h"
   13 #include "read-only-mem-types.h"
   14 #include "read-only.h"
   15 #include <glusterfs/syncop.h>
   16 #include "worm-helper.h"
   17 
   18 int32_t
   19 mem_acct_init(xlator_t *this)
   20 {
   21     int ret = -1;
   22 
   23     ret = xlator_mem_acct_init(this, gf_read_only_mt_end + 1);
   24     if (ret)
   25         gf_log(this->name, GF_LOG_ERROR,
   26                "Memory accounting "
   27                "initialization failed.");
   28 
   29     return ret;
   30 }
   31 
   32 static int32_t
   33 worm_open(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
   34           fd_t *fd, dict_t *xdata)
   35 {
   36     if (is_readonly_or_worm_enabled(frame, this) &&
   37         (flags & (O_WRONLY | O_RDWR | O_APPEND | O_TRUNC))) {
   38         STACK_UNWIND_STRICT(open, frame, -1, EROFS, NULL, NULL);
   39         return 0;
   40     }
   41 
   42     STACK_WIND_TAIL(frame, FIRST_CHILD(this), FIRST_CHILD(this)->fops->open,
   43                     loc, flags, fd, xdata);
   44     return 0;
   45 }
   46 
   47 static int32_t
   48 worm_link(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
   49           dict_t *xdata)
   50 {
   51     int op_errno = EROFS;
   52     read_only_priv_t *priv = NULL;
   53 
   54     priv = this->private;
   55     GF_ASSERT(priv);
   56     if (is_readonly_or_worm_enabled(frame, this))
   57         goto out;
   58     if (!priv->worm_file || (frame->root->pid < 0)) {
   59         op_errno = 0;
   60         goto out;
   61     }
   62 
   63     gf_uuid_copy(oldloc->gfid, oldloc->inode->gfid);
   64     if (is_wormfile(this, _gf_false, oldloc)) {
   65         op_errno = 0;
   66         goto out;
   67     }
   68     op_errno = gf_worm_state_transition(this, _gf_false, oldloc, GF_FOP_LINK);
   69 
   70 out:
   71     if (op_errno) {
   72         if (op_errno < 0)
   73             op_errno = EROFS;
   74         STACK_UNWIND_STRICT(link, frame, -1, op_errno, NULL, NULL, NULL, NULL,
   75                             NULL);
   76     } else
   77         STACK_WIND_TAIL(frame, FIRST_CHILD(this), FIRST_CHILD(this)->fops->link,
   78                         oldloc, newloc, xdata);
   79     return 0;
   80 }
   81 
   82 static int32_t
   83 worm_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
   84             dict_t *xdata)
   85 {
   86     int op_errno = EROFS;
   87     read_only_priv_t *priv = NULL;
   88 
   89     priv = this->private;
   90     GF_ASSERT(priv);
   91     if (is_readonly_or_worm_enabled(frame, this)) {
   92         goto out;
   93     }
   94     if (!priv->worm_file || (frame->root->pid < 0)) {
   95         op_errno = 0;
   96         goto out;
   97     }
   98 
   99     gf_uuid_copy(loc->gfid, loc->inode->gfid);
  100     if (is_wormfile(this, _gf_false, loc)) {
  101         op_errno = 0;
  102         goto out;
  103     }
  104     op_errno = gf_worm_state_transition(this, _gf_false, loc, GF_FOP_UNLINK);
  105 out:
  106     if (op_errno) {
  107         if (op_errno < 0)
  108             op_errno = EROFS;
  109         STACK_UNWIND_STRICT(unlink, frame, -1, op_errno, NULL, NULL, NULL);
  110     } else
  111         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  112                         FIRST_CHILD(this)->fops->unlink, loc, flags, xdata);
  113     return 0;
  114 }
  115 
  116 static int32_t
  117 worm_rename(call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
  118             dict_t *xdata)
  119 {
  120     int op_errno = EROFS;
  121     read_only_priv_t *priv = NULL;
  122 
  123     priv = this->private;
  124     GF_ASSERT(priv);
  125     if (is_readonly_or_worm_enabled(frame, this))
  126         goto out;
  127     if (!priv->worm_file || (frame->root->pid < 0)) {
  128         op_errno = 0;
  129         goto out;
  130     }
  131 
  132     gf_uuid_copy(oldloc->gfid, oldloc->inode->gfid);
  133     if (is_wormfile(this, _gf_false, oldloc)) {
  134         op_errno = 0;
  135         goto check_newloc;
  136     }
  137     op_errno = gf_worm_state_transition(this, _gf_false, oldloc, GF_FOP_RENAME);
  138 
  139     if (op_errno == 0) {
  140     check_newloc:
  141         if (newloc->inode != NULL) {
  142             gf_uuid_copy(newloc->gfid, newloc->inode->gfid);
  143             if (is_wormfile(this, _gf_false, newloc)) {
  144                 op_errno = 0;
  145                 goto out;
  146             }
  147             op_errno = gf_worm_state_transition(this, _gf_false, newloc,
  148                                                 GF_FOP_RENAME);
  149         }
  150     }
  151 
  152 out:
  153     if (op_errno) {
  154         if (op_errno < 0)
  155             op_errno = EROFS;
  156         STACK_UNWIND_STRICT(rename, frame, -1, op_errno, NULL, NULL, NULL, NULL,
  157                             NULL, NULL);
  158     } else
  159         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  160                         FIRST_CHILD(this)->fops->rename, oldloc, newloc, xdata);
  161     return 0;
  162 }
  163 
  164 static int32_t
  165 worm_truncate(call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset,
  166               dict_t *xdata)
  167 {
  168     int op_errno = EROFS;
  169     read_only_priv_t *priv = NULL;
  170 
  171     priv = this->private;
  172     GF_ASSERT(priv);
  173     if (is_readonly_or_worm_enabled(frame, this))
  174         goto out;
  175     if (!priv->worm_file || (frame->root->pid < 0)) {
  176         op_errno = 0;
  177         goto out;
  178     }
  179 
  180     if (is_wormfile(this, _gf_false, loc)) {
  181         op_errno = 0;
  182         goto out;
  183     }
  184     op_errno = gf_worm_state_transition(this, _gf_false, loc, GF_FOP_TRUNCATE);
  185 
  186 out:
  187     if (op_errno) {
  188         if (op_errno < 0)
  189             op_errno = EROFS;
  190         STACK_UNWIND_STRICT(truncate, frame, -1, op_errno, NULL, NULL, NULL);
  191     } else
  192         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  193                         FIRST_CHILD(this)->fops->truncate, loc, offset, xdata);
  194     return 0;
  195 }
  196 
  197 static int32_t
  198 worm_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset,
  199                dict_t *xdata)
  200 {
  201     int op_errno = EROFS;
  202     read_only_priv_t *priv = NULL;
  203 
  204     priv = this->private;
  205     GF_ASSERT(priv);
  206     if (is_readonly_or_worm_enabled(frame, this))
  207         goto out;
  208     if (!priv->worm_file || (frame->root->pid < 0)) {
  209         op_errno = 0;
  210         goto out;
  211     }
  212 
  213     if (is_wormfile(this, _gf_true, fd)) {
  214         op_errno = 0;
  215         goto out;
  216     }
  217     op_errno = gf_worm_state_transition(this, _gf_true, fd, GF_FOP_FTRUNCATE);
  218 
  219 out:
  220     if (op_errno) {
  221         if (op_errno < 0)
  222             op_errno = EROFS;
  223         STACK_UNWIND_STRICT(ftruncate, frame, -1, op_errno, NULL, NULL, NULL);
  224     } else
  225         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  226                         FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata);
  227     return 0;
  228 }
  229 
  230 static int32_t
  231 worm_setattr(call_frame_t *frame, xlator_t *this, loc_t *loc,
  232              struct iatt *stbuf, int32_t valid, dict_t *xdata)
  233 {
  234     gf_boolean_t rd_only = _gf_false;
  235     worm_reten_state_t reten_state = {
  236         0,
  237     };
  238     struct iatt stpre = {
  239         0,
  240     };
  241     read_only_priv_t *priv = NULL;
  242     int op_errno = EROFS;
  243     int ret = -1;
  244 
  245     priv = this->private;
  246     GF_ASSERT(priv);
  247     if (!priv->worm_file) {
  248         op_errno = 0;
  249         goto out;
  250     }
  251 
  252     if (is_wormfile(this, _gf_false, loc)) {
  253         op_errno = 0;
  254         goto out;
  255     }
  256     if (valid & GF_SET_ATTR_MODE) {
  257         rd_only = gf_worm_write_disabled(stbuf);
  258         if (!rd_only) {
  259             op_errno = 0;
  260             goto out;
  261         }
  262 
  263         ret = worm_set_state(this, _gf_false, loc, &reten_state, stbuf);
  264         if (ret) {
  265             gf_log(this->name, GF_LOG_ERROR, "Error setting worm state");
  266             goto out;
  267         }
  268     } else if (valid & GF_SET_ATTR_ATIME) {
  269         ret = worm_get_state(this, _gf_false, loc, &reten_state);
  270         if (ret) {
  271             op_errno = 0;
  272             goto out;
  273         }
  274         if (reten_state.retain) {
  275             ret = syncop_stat(this, loc, &stpre, NULL, NULL);
  276             if (ret)
  277                 goto out;
  278             if (reten_state.ret_mode == 0) {
  279                 if (stbuf->ia_atime < stpre.ia_mtime) {
  280                     gf_log(this->name, GF_LOG_ERROR,
  281                            "Cannot set atime less than "
  282                            "the mtime for a WORM-Retained "
  283                            "file");
  284                     goto out;
  285                 }
  286             } else {
  287                 if (stbuf->ia_atime < stpre.ia_atime) {
  288                     gf_log(this->name, GF_LOG_ERROR,
  289                            "Cannot decrease the atime of a"
  290                            " WORM-Retained file in "
  291                            "Enterprise mode");
  292                     goto out;
  293                 }
  294             }
  295             reten_state.ret_period = reten_state.ret_period + stbuf->ia_atime -
  296                                      stpre.ia_atime;
  297             ret = gf_worm_set_xattr(this, &reten_state, _gf_false, loc);
  298             if (ret) {
  299                 goto out;
  300             }
  301             stbuf->ia_mtime = stpre.ia_mtime;
  302         }
  303     }
  304     op_errno = 0;
  305 
  306 out:
  307     if (op_errno)
  308         STACK_UNWIND_STRICT(setattr, frame, -1, EROFS, NULL, NULL, NULL);
  309     else
  310         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  311                         FIRST_CHILD(this)->fops->setattr, loc, stbuf, valid,
  312                         xdata);
  313     return 0;
  314 }
  315 
  316 static int32_t
  317 worm_fsetattr(call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *stbuf,
  318               int32_t valid, dict_t *xdata)
  319 {
  320     gf_boolean_t rd_only = _gf_false;
  321     worm_reten_state_t reten_state = {
  322         0,
  323     };
  324     struct iatt stpre = {
  325         0,
  326     };
  327     read_only_priv_t *priv = NULL;
  328     int op_errno = EROFS;
  329     int ret = -1;
  330 
  331     priv = this->private;
  332     GF_ASSERT(priv);
  333     if (!priv->worm_file) {
  334         op_errno = 0;
  335         goto out;
  336     }
  337 
  338     if (is_wormfile(this, _gf_true, fd)) {
  339         op_errno = 0;
  340         goto out;
  341     }
  342     if (valid & GF_SET_ATTR_MODE) {
  343         rd_only = gf_worm_write_disabled(stbuf);
  344         if (!rd_only) {
  345             op_errno = 0;
  346             goto out;
  347         }
  348 
  349         ret = worm_set_state(this, _gf_true, fd, &reten_state, stbuf);
  350         if (ret) {
  351             gf_log(this->name, GF_LOG_ERROR, "Error setting worm state");
  352             goto out;
  353         }
  354     } else if (valid & GF_SET_ATTR_ATIME) {
  355         ret = worm_get_state(this, _gf_true, fd, &reten_state);
  356         if (ret) {
  357             op_errno = 0;
  358             goto out;
  359         }
  360         if (reten_state.retain) {
  361             ret = syncop_fstat(this, fd, &stpre, NULL, NULL);
  362             if (ret)
  363                 goto out;
  364             if (reten_state.ret_mode == 0) {
  365                 if (stbuf->ia_atime < stpre.ia_mtime) {
  366                     gf_log(this->name, GF_LOG_ERROR,
  367                            "Cannot set atime less than "
  368                            "the mtime for a WORM-Retained "
  369                            "file");
  370                     goto out;
  371                 }
  372             } else {
  373                 if (stbuf->ia_atime < stpre.ia_atime) {
  374                     gf_log(this->name, GF_LOG_ERROR,
  375                            "Cannot decrease the atime of a"
  376                            " WORM-Retained file in "
  377                            "Enterprise mode");
  378                     goto out;
  379                 }
  380             }
  381             reten_state.ret_period = reten_state.ret_period + stbuf->ia_atime -
  382                                      stpre.ia_atime;
  383             ret = gf_worm_set_xattr(this, &reten_state, _gf_true, fd);
  384             if (ret) {
  385                 goto out;
  386             }
  387 
  388             stbuf->ia_mtime = stpre.ia_mtime;
  389         }
  390     }
  391     op_errno = 0;
  392 
  393 out:
  394     if (op_errno)
  395         STACK_UNWIND_STRICT(fsetattr, frame, -1, op_errno, NULL, NULL, NULL);
  396     else
  397         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  398                         FIRST_CHILD(this)->fops->fsetattr, fd, stbuf, valid,
  399                         xdata);
  400     return 0;
  401 }
  402 
  403 static int32_t
  404 worm_writev(call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector,
  405             int32_t count, off_t offset, uint32_t flags, struct iobref *iobref,
  406             dict_t *xdata)
  407 {
  408     read_only_priv_t *priv = NULL;
  409     int op_errno = EROFS;
  410 
  411     priv = this->private;
  412     GF_ASSERT(priv);
  413     if (!priv->worm_file || (frame->root->pid < 0)) {
  414         op_errno = 0;
  415         goto out;
  416     }
  417     if (is_wormfile(this, _gf_true, fd)) {
  418         op_errno = 0;
  419         goto out;
  420     }
  421     op_errno = gf_worm_state_transition(this, _gf_true, fd, GF_FOP_WRITE);
  422 
  423 out:
  424     if (op_errno) {
  425         if (op_errno < 0)
  426             op_errno = EROFS;
  427         STACK_UNWIND_STRICT(writev, frame, -1, op_errno, NULL, NULL, NULL);
  428     } else
  429         STACK_WIND_TAIL(frame, FIRST_CHILD(this),
  430                         FIRST_CHILD(this)->fops->writev, fd, vector, count,
  431                         offset, flags, iobref, xdata);
  432     return 0;
  433 }
  434 
  435 static int32_t
  436 worm_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this,
  437                 int32_t op_ret, int32_t op_errno, fd_t *fd, inode_t *inode,
  438                 struct iatt *buf, struct iatt *preparent,
  439                 struct iatt *postparent, dict_t *xdata)
  440 {
  441     int ret = 0;
  442     read_only_priv_t *priv = NULL;
  443     // In case of an error exit because fd can be NULL and this would
  444     // cause an segfault when performing fsetxattr . We explicitly
  445     // unwind to avoid future problems
  446     if (op_ret < 0) {
  447         goto out;
  448     }
  449 
  450     priv = this->private;
  451     GF_ASSERT(priv);
  452     if (priv->worm_file) {
  453         ret = fd_ctx_set(fd, this, 1);
  454         if (ret) {
  455             gf_log(this->name, GF_LOG_ERROR,
  456                    "Failed to set the fd ctx "
  457                    "for gfid:%s . Worm feature may not work for the gfid",
  458                    uuid_utoa(inode->gfid));
  459         }
  460         ret = worm_init_state(this, _gf_true, fd);
  461         if (ret) {
  462             gf_log(this->name, GF_LOG_ERROR, "Error initializing state");
  463         }
  464     }
  465 
  466 out:
  467     STACK_UNWIND_STRICT(create, frame, op_ret, op_errno, fd, inode, buf,
  468                         preparent, postparent, xdata);
  469     return ret;
  470 }
  471 
  472 static int32_t
  473 worm_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
  474             mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata)
  475 {
  476     STACK_WIND(frame, worm_create_cbk, FIRST_CHILD(this),
  477                FIRST_CHILD(this)->fops->create, loc, flags, mode, umask, fd,
  478                xdata);
  479     return 0;
  480 }
  481 
  482 static void
  483 set_reten_mode(read_only_priv_t *priv, char *reten_mode)
  484 {
  485     if (strcmp(reten_mode, "relax") == 0)
  486         priv->reten_mode = 0;
  487     else
  488         priv->reten_mode = 1;
  489 }
  490 
  491 int32_t
  492 init(xlator_t *this)
  493 {
  494     int ret = -1;
  495     read_only_priv_t *priv = NULL;
  496     char *reten_mode = NULL;
  497 
  498     if (!this->children || this->children->next) {
  499         gf_log(this->name, GF_LOG_ERROR,
  500                "translator not configured with exactly one child");
  501         return -1;
  502     }
  503 
  504     if (!this->parents) {
  505         gf_log(this->name, GF_LOG_WARNING, "dangling volume. check volfile ");
  506     }
  507 
  508     this->local_pool = mem_pool_new(read_only_priv_t, 64);
  509     if (!this->local_pool) {
  510         ret = -1;
  511         gf_log(this->name, GF_LOG_ERROR,
  512                "failed to create read_only_priv_t's memory pool");
  513         goto out;
  514     }
  515 
  516     priv = mem_get0(this->local_pool);
  517     if (!priv) {
  518         gf_log(this->name, GF_LOG_ERROR, "Error allocating priv");
  519         goto out;
  520     }
  521 
  522     this->private = priv;
  523 
  524     GF_OPTION_INIT("worm", priv->readonly_or_worm_enabled, bool, out);
  525     GF_OPTION_INIT("worm-file-level", priv->worm_file, bool, out);
  526     GF_OPTION_INIT("default-retention-period", priv->reten_period, int64, out);
  527     GF_OPTION_INIT("auto-commit-period", priv->com_period, int64, out);
  528     GF_OPTION_INIT("retention-mode", reten_mode, str, out);
  529     set_reten_mode(priv, reten_mode);
  530     GF_OPTION_INIT("worm-files-deletable", priv->worm_files_deletable, bool,
  531                    out);
  532 
  533     ret = 0;
  534 out:
  535     return ret;
  536 }
  537 
  538 int
  539 reconfigure(xlator_t *this, dict_t *options)
  540 {
  541     read_only_priv_t *priv = NULL;
  542     char *reten_mode = NULL;
  543     int ret = -1;
  544 
  545     priv = this->private;
  546     GF_ASSERT(priv);
  547 
  548     GF_OPTION_RECONF("worm", priv->readonly_or_worm_enabled, options, bool,
  549                      out);
  550     GF_OPTION_RECONF("worm-file-level", priv->worm_file, options, bool, out);
  551     GF_OPTION_RECONF("default-retention-period", priv->reten_period, options,
  552                      int64, out);
  553     GF_OPTION_RECONF("retention-mode", reten_mode, options, str, out);
  554     set_reten_mode(priv, reten_mode);
  555     GF_OPTION_RECONF("auto-commit-period", priv->com_period, options, int64,
  556                      out);
  557     GF_OPTION_RECONF("worm-files-deletable", priv->worm_files_deletable,
  558                      options, bool, out);
  559     ret = 0;
  560 out:
  561     gf_log(this->name, GF_LOG_DEBUG, "returning %d", ret);
  562     return ret;
  563 }
  564 
  565 void
  566 fini(xlator_t *this)
  567 {
  568     read_only_priv_t *priv = NULL;
  569 
  570     priv = this->private;
  571     if (!priv)
  572         goto out;
  573     mem_put(priv);
  574     this->private = NULL;
  575     mem_pool_destroy(this->local_pool);
  576     this->local_pool = NULL;
  577 out:
  578     return;
  579 }
  580 
  581 struct xlator_fops fops = {
  582     .open = worm_open,
  583     .writev = worm_writev,
  584     .setattr = worm_setattr,
  585     .fsetattr = worm_fsetattr,
  586     .rename = worm_rename,
  587     .link = worm_link,
  588     .unlink = worm_unlink,
  589     .truncate = worm_truncate,
  590     .ftruncate = worm_ftruncate,
  591     .create = worm_create,
  592 
  593     .rmdir = ro_rmdir,
  594     .removexattr = ro_removexattr,
  595     .fsyncdir = ro_fsyncdir,
  596     .xattrop = ro_xattrop,
  597     .inodelk = ro_inodelk,
  598     .finodelk = ro_finodelk,
  599     .entrylk = ro_entrylk,
  600     .fentrylk = ro_fentrylk,
  601     .lk = ro_lk,
  602 };
  603 
  604 int32_t
  605 worm_release(xlator_t *this, fd_t *fd)
  606 {
  607     dict_t *dict = NULL;
  608     int ret = -1;
  609     dict = dict_new();
  610     uint64_t value = 0;
  611     loc_t loc = {
  612         0,
  613     };
  614     read_only_priv_t *priv = NULL;
  615     priv = this->private;
  616 
  617     if (priv->worm_file) {
  618         if (!dict) {
  619             gf_log(this->name, GF_LOG_ERROR, "Error creating the dict");
  620             goto out;
  621         }
  622 
  623         ret = fd_ctx_get(fd, this, &value);
  624         if (ret) {
  625             gf_log(this->name, GF_LOG_DEBUG, "Failed to get the fd ctx");
  626         }
  627         if (!value) {
  628             goto out;
  629         }
  630 
  631         ret = dict_set_int8(dict, "trusted.worm_file", 1);
  632         if (ret) {
  633             gf_log(this->name, GF_LOG_ERROR,
  634                    "Error in setting "
  635                    "the dict");
  636             goto out;
  637         }
  638 
  639         loc.inode = inode_ref(fd->inode);
  640         gf_uuid_copy(loc.gfid, fd->inode->gfid);
  641         ret = syncop_setxattr(this, &loc, dict, 0, NULL, NULL);
  642         if (ret) {
  643             gf_log(this->name, GF_LOG_ERROR, "Error setting xattr");
  644             goto out;
  645         }
  646 
  647         gf_worm_state_transition(this, _gf_false, &loc, GF_FOP_WRITE);
  648     }
  649 
  650 out:
  651     loc_wipe(&loc);
  652     if (dict)
  653         dict_unref(dict);
  654     return 0;
  655 }
  656 
  657 struct xlator_cbks cbks = {
  658     .release = worm_release,
  659 };
  660 
  661 struct volume_options options[] = {
  662     {.key = {"worm"},
  663      .type = GF_OPTION_TYPE_BOOL,
  664      .default_value = "off",
  665      /*.validate_fn = validate_boolean,*/
  666      .op_version = {2},
  667      .flags = OPT_FLAG_SETTABLE,
  668      .description = "When \"on\", makes a volume get write once read many "
  669                     " feature. It is turned \"off\" by default."},
  670     {.key = {"worm-file-level"},
  671      .type = GF_OPTION_TYPE_BOOL,
  672      .default_value = "off",
  673      /*.validate_fn = validate_boolean,*/
  674      .op_version = {GD_OP_VERSION_3_8_0},
  675      .flags = OPT_FLAG_SETTABLE,
  676      .description = "When \"on\", activates the file level worm. "
  677                     "It is turned \"off\" by default."},
  678     {.key = {"worm-files-deletable"},
  679      .type = GF_OPTION_TYPE_BOOL,
  680      .default_value = "on",
  681      /*.validate_fn = validate_boolean,*/
  682      .op_version = {GD_OP_VERSION_3_13_0},
  683      .flags = OPT_FLAG_SETTABLE,
  684      .description = "When \"off\", doesn't allow the Worm files"
  685                     "to be deleted. It is turned \"on\" by default."},
  686     {.key = {"default-retention-period"},
  687      .type = GF_OPTION_TYPE_TIME,
  688      .default_value = "120",
  689      /*.validate_fn = validate_worm_period,*/
  690      .op_version = {GD_OP_VERSION_3_8_0},
  691      .flags = OPT_FLAG_SETTABLE,
  692      .description = "The default retention period for the files."},
  693     {.key = {"retention-mode"},
  694      .type = GF_OPTION_TYPE_STR,
  695      .default_value = "relax",
  696      /*.validate_fn = validate_reten_mode,*/
  697      .op_version = {GD_OP_VERSION_3_8_0},
  698      .flags = OPT_FLAG_SETTABLE,
  699      .description = "The mode of retention (relax/enterprise). "
  700                     "It is relax by default."},
  701     {.key = {"auto-commit-period"},
  702      .type = GF_OPTION_TYPE_TIME,
  703      .default_value = "180",
  704      /*.validate_fn = validate_worm_period,*/
  705      .op_version = {GD_OP_VERSION_3_8_0},
  706      .flags = OPT_FLAG_SETTABLE,
  707      .description = "Auto commit period for the files."},
  708     {.key = {NULL}},
  709 };
  710 
  711 xlator_api_t xlator_api = {
  712     .init = init,
  713     .fini = fini,
  714     .reconfigure = reconfigure,
  715     .mem_acct_init = mem_acct_init,
  716     .op_version = {1}, /* Present from the initial version */
  717     .fops = &fops,
  718     .cbks = &cbks,
  719     .options = options,
  720     .identifier = "worm",
  721     .category = GF_TECH_PREVIEW,
  722 };