+ /* buf[2] is the pool name, reuse it directly */
+ lustre_cfg_bufs_set(n_bufs, 2, lustre_cfg_buf(o_lcfg, 2),
+ o_lcfg->lcfg_buflens[2]);
+
+ if (cnt == 3)
+ break;
+
+ /* buf[3] is ostname */
+ o_buf = lustre_cfg_buf(o_lcfg, 3);
+ o_buflen = o_lcfg->lcfg_buflens[3];
+ memcpy(n_buf, mlfd->mlfd_newname, n_namelen);
+ memcpy(n_buf + n_namelen, o_buf + o_namelen,
+ o_buflen - o_namelen);
+ lustre_cfg_bufs_set(n_bufs, 3, n_buf, o_buflen + diff);
+ break;
+ }
+ case LCFG_SETUP: {
+ if (cnt == 2) {
+ o_buflen = o_lcfg->lcfg_buflens[1];
+ if (o_buflen == sizeof(struct lov_desc) ||
+ o_buflen == sizeof(struct lmv_desc)) {
+ char *o_uuid;
+ char *n_uuid;
+ int uuid_len;
+
+ /* buf[1] */
+ o_buf = lustre_cfg_buf(o_lcfg, 1);
+ if (o_buflen == sizeof(struct lov_desc)) {
+ struct lov_desc *o_desc =
+ (struct lov_desc *)o_buf;
+ struct lov_desc *n_desc =
+ (struct lov_desc *)n_buf;
+
+ *n_desc = *o_desc;
+ o_uuid = o_desc->ld_uuid.uuid;
+ n_uuid = n_desc->ld_uuid.uuid;
+ uuid_len = sizeof(o_desc->ld_uuid.uuid);
+ } else {
+ struct lmv_desc *o_desc =
+ (struct lmv_desc *)o_buf;
+ struct lmv_desc *n_desc =
+ (struct lmv_desc *)n_buf;
+
+ *n_desc = *o_desc;
+ o_uuid = o_desc->ld_uuid.uuid;
+ n_uuid = n_desc->ld_uuid.uuid;
+ uuid_len = sizeof(o_desc->ld_uuid.uuid);
+ }
+
+ if (unlikely(!contain_valid_fsname(o_uuid,
+ mlfd->mlfd_oldname, uuid_len,
+ o_namelen))) {
+ lustre_cfg_bufs_set(n_bufs, 1, o_buf,
+ o_buflen);
+ break;
+ }
+
+ memcpy(n_uuid, mlfd->mlfd_newname, n_namelen);
+ uuid_len = strlen(o_uuid);
+ if (uuid_len > o_namelen)
+ memcpy(n_uuid + n_namelen,
+ o_uuid + o_namelen,
+ uuid_len - o_namelen);
+ n_uuid[uuid_len + diff] = '\0';
+ lustre_cfg_bufs_set(n_bufs, 1, n_buf, o_buflen);
+ break;
+ } /* else case fall through */
+ } /* else case fall through */
+ }
+ default: {
+ for (i = 1; i < cnt; i++) {
+ o_buflen = o_lcfg->lcfg_buflens[i];
+ if (o_buflen == 0)
+ continue;
+
+ o_buf = lustre_cfg_buf(o_lcfg, i);
+ if (!contain_valid_fsname(o_buf, mlfd->mlfd_oldname,
+ o_buflen, o_namelen)) {
+ lustre_cfg_bufs_set(n_bufs, i, o_buf, o_buflen);
+ continue;
+ }
+
+ memcpy(n_buf, mlfd->mlfd_newname, n_namelen);
+ if (o_buflen == o_namelen) {
+ lustre_cfg_bufs_set(n_bufs, i, n_buf,
+ n_namelen);
+ n_buf += cfs_size_round(n_namelen);
+ continue;
+ }
+
+ memcpy(n_buf + n_namelen, o_buf + o_namelen,
+ o_buflen - o_namelen);
+ lustre_cfg_bufs_set(n_bufs, i, n_buf, o_buflen + diff);
+ n_buf += cfs_size_round(o_buflen + diff);
+ }
+ break;
+ }
+ }
+
+ lcr = lustre_cfg_rec_new(cmd, n_bufs);
+ if (!lcr)
+ RETURN(-ENOMEM);
+
+ lcr->lcr_cfg = *o_lcfg;
+ rc = llog_write(env, mlfd->mlfd_llh, &lcr->lcr_hdr, LLOG_NEXT_IDX);
+ lustre_cfg_rec_free(lcr);
+
+ RETURN(rc);
+}
+
+static int mgs_lcfg_fork_one(const struct lu_env *env, struct mgs_device *mgs,
+ struct mgs_direntry *mde, const char *oldname,
+ const char *newname)
+{
+ struct llog_handle *old_llh = NULL;
+ struct llog_handle *new_llh = NULL;
+ struct llog_ctxt *ctxt = NULL;
+ struct mgs_lcfg_fork_data *mlfd = NULL;
+ char *name_buf = NULL;
+ int name_buflen;
+ int old_namelen = strlen(oldname);
+ int new_namelen = strlen(newname);
+ int rc;
+ ENTRY;
+
+ name_buflen = mde->mde_len + new_namelen - old_namelen;
+ OBD_ALLOC(name_buf, name_buflen);
+ if (!name_buf)
+ RETURN(-ENOMEM);
+
+ memcpy(name_buf, newname, new_namelen);
+ memcpy(name_buf + new_namelen, mde->mde_name + old_namelen,
+ mde->mde_len - old_namelen);
+
+ CDEBUG(D_MGS, "Fork the config-log from %s to %s\n",
+ mde->mde_name, name_buf);
+
+ ctxt = llog_get_context(mgs->mgs_obd, LLOG_CONFIG_ORIG_CTXT);
+ LASSERT(ctxt);
+
+ rc = llog_open_create(env, ctxt, &new_llh, NULL, name_buf);
+ if (rc)
+ GOTO(out, rc);
+
+ rc = llog_init_handle(env, new_llh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ GOTO(out, rc);
+
+ if (unlikely(mgs_log_is_empty(env, mgs, mde->mde_name)))
+ GOTO(out, rc = 0);
+
+ rc = llog_open(env, ctxt, &old_llh, NULL, mde->mde_name,
+ LLOG_OPEN_EXISTS);
+ if (rc)
+ GOTO(out, rc);
+
+ rc = llog_init_handle(env, old_llh, LLOG_F_IS_PLAIN, NULL);
+ if (rc)
+ GOTO(out, rc);
+
+ new_llh->lgh_hdr->llh_tgtuuid = old_llh->lgh_hdr->llh_tgtuuid;
+
+ OBD_ALLOC(mlfd, LLOG_MIN_CHUNK_SIZE);
+ if (!mlfd)
+ GOTO(out, rc = -ENOMEM);
+
+ mlfd->mlfd_mgs = mgs;
+ mlfd->mlfd_llh = new_llh;
+ mlfd->mlfd_oldname = oldname;
+ mlfd->mlfd_newname = newname;
+
+ rc = llog_process(env, old_llh, mgs_lcfg_fork_handler, mlfd, NULL);
+ OBD_FREE(mlfd, LLOG_MIN_CHUNK_SIZE);
+
+ GOTO(out, rc);
+
+out:
+ if (old_llh)
+ llog_close(env, old_llh);
+ if (new_llh)
+ llog_close(env, new_llh);
+ if (name_buf)
+ OBD_FREE(name_buf, name_buflen);
+ if (ctxt)
+ llog_ctxt_put(ctxt);
+
+ return rc;
+}
+
+int mgs_lcfg_fork(const struct lu_env *env, struct mgs_device *mgs,
+ const char *oldname, const char *newname)
+{
+ struct list_head log_list;
+ struct mgs_direntry *dirent, *n;
+ int olen = strlen(oldname);
+ int nlen = strlen(newname);
+ int count = 0;
+ int rc = 0;
+ ENTRY;
+
+ if (unlikely(!oldname || oldname[0] == '\0' ||
+ !newname || newname[0] == '\0'))
+ RETURN(-EINVAL);
+
+ if (strcmp(oldname, newname) == 0)
+ RETURN(-EINVAL);
+
+ /* lock it to prevent fork/erase/register in parallel. */
+ mutex_lock(&mgs->mgs_mutex);
+
+ rc = class_dentry_readdir(env, mgs, &log_list);
+ if (rc) {
+ mutex_unlock(&mgs->mgs_mutex);
+ RETURN(rc);
+ }
+
+ if (list_empty(&log_list)) {
+ mutex_unlock(&mgs->mgs_mutex);
+ RETURN(-ENOENT);
+ }
+
+ list_for_each_entry_safe(dirent, n, &log_list, mde_list) {
+ char *ptr;
+
+ ptr = strrchr(dirent->mde_name, '-');
+ if (ptr) {
+ int tlen = ptr - dirent->mde_name;
+
+ if (tlen == nlen &&
+ strncmp(newname, dirent->mde_name, tlen) == 0)
+ GOTO(out, rc = -EEXIST);
+
+ if (tlen == olen &&
+ strncmp(oldname, dirent->mde_name, tlen) == 0)
+ continue;
+ }
+
+ list_del_init(&dirent->mde_list);
+ mgs_direntry_free(dirent);
+ }
+
+ if (list_empty(&log_list)) {
+ mutex_unlock(&mgs->mgs_mutex);
+ RETURN(-ENOENT);
+ }
+
+ list_for_each_entry(dirent, &log_list, mde_list) {
+ rc = mgs_lcfg_fork_one(env, mgs, dirent, oldname, newname);
+ if (rc)
+ break;
+
+ count++;
+ }
+
+out:
+ mutex_unlock(&mgs->mgs_mutex);
+
+ list_for_each_entry_safe(dirent, n, &log_list, mde_list) {
+ list_del_init(&dirent->mde_list);
+ mgs_direntry_free(dirent);
+ }
+
+ if (rc && count > 0)
+ mgs_erase_logs(env, mgs, newname);
+
+ RETURN(rc);
+}
+
+int mgs_lcfg_erase(const struct lu_env *env, struct mgs_device *mgs,
+ const char *fsname)
+{
+ int rc;
+ ENTRY;
+
+ if (unlikely(!fsname || fsname[0] == '\0'))
+ RETURN(-EINVAL);
+
+ rc = mgs_erase_logs(env, mgs, fsname);
+
+ RETURN(rc);
+}
+
+static int mgs_xattr_del(const struct lu_env *env, struct dt_object *obj)
+{
+ struct dt_device *dev;
+ struct thandle *th = NULL;
+ int rc = 0;
+
+ ENTRY;
+
+ dev = container_of0(obj->do_lu.lo_dev, struct dt_device, dd_lu_dev);
+ th = dt_trans_create(env, dev);
+ if (IS_ERR(th))
+ RETURN(PTR_ERR(th));
+
+ rc = dt_declare_xattr_del(env, obj, XATTR_TARGET_RENAME, th);
+ if (rc)
+ GOTO(stop, rc);
+
+ rc = dt_trans_start_local(env, dev, th);
+ if (rc)
+ GOTO(stop, rc);
+
+ dt_write_lock(env, obj, 0);
+ rc = dt_xattr_del(env, obj, XATTR_TARGET_RENAME, th);
+
+ GOTO(unlock, rc);
+
+unlock:
+ dt_write_unlock(env, obj);
+
+stop:
+ dt_trans_stop(env, dev, th);
+
+ return rc;
+}
+
+int mgs_lcfg_rename(const struct lu_env *env, struct mgs_device *mgs)
+{
+ struct list_head log_list;
+ struct mgs_direntry *dirent, *n;
+ char fsname[16];
+ struct lu_buf buf = {
+ .lb_buf = fsname,
+ .lb_len = sizeof(fsname)
+ };
+ int rc = 0;
+
+ ENTRY;
+
+ rc = class_dentry_readdir(env, mgs, &log_list);
+ if (rc)
+ RETURN(rc);
+
+ if (list_empty(&log_list))
+ RETURN(0);
+
+ list_for_each_entry_safe(dirent, n, &log_list, mde_list) {
+ struct dt_object *o = NULL;
+ char oldname[16];
+ char *ptr;
+ int len;
+
+ list_del_init(&dirent->mde_list);
+ ptr = strrchr(dirent->mde_name, '-');
+ if (!ptr)
+ goto next;
+
+ len = ptr - dirent->mde_name;
+ if (unlikely(len >= sizeof(oldname))) {
+ CDEBUG(D_MGS, "Skip invalid configuration file %s\n",
+ dirent->mde_name);
+ goto next;
+ }
+
+ o = local_file_find(env, mgs->mgs_los, mgs->mgs_configs_dir,
+ dirent->mde_name);
+ if (IS_ERR(o)) {
+ rc = PTR_ERR(o);
+ CDEBUG(D_MGS, "Fail to locate file %s: rc = %d\n",
+ dirent->mde_name, rc);
+ goto next;
+ }
+
+ rc = dt_xattr_get(env, o, &buf, XATTR_TARGET_RENAME);
+ if (rc < 0) {
+ if (rc == -ENODATA)
+ rc = 0;
+ else
+ CDEBUG(D_MGS,
+ "Fail to get EA for %s: rc = %d\n",
+ dirent->mde_name, rc);
+ goto next;
+ }
+
+ if (unlikely(rc == len &&
+ memcmp(fsname, dirent->mde_name, len) == 0)) {
+ /* The new fsname is the same as the old one. */
+ rc = mgs_xattr_del(env, o);
+ goto next;
+ }
+
+ memcpy(oldname, dirent->mde_name, len);
+ oldname[len] = '\0';
+ fsname[rc] = '\0';
+ rc = mgs_lcfg_fork_one(env, mgs, dirent, oldname, fsname);
+ if (rc && rc != -EEXIST) {
+ CDEBUG(D_MGS, "Fail to fork %s: rc = %d\n",
+ dirent->mde_name, rc);
+ goto next;
+ }
+
+ rc = mgs_erase_log(env, mgs, dirent->mde_name);
+ if (rc) {
+ CDEBUG(D_MGS, "Fail to erase old %s: rc = %d\n",
+ dirent->mde_name, rc);
+ /* keep it there if failed to remove it. */
+ rc = 0;
+ }
+
+next:
+ if (o && !IS_ERR(o))
+ lu_object_put(env, &o->do_lu);
+
+ mgs_direntry_free(dirent);
+ if (rc)
+ break;
+ }
+
+ list_for_each_entry_safe(dirent, n, &log_list, mde_list) {
+ list_del_init(&dirent->mde_list);
+ mgs_direntry_free(dirent);
+ }
+
+ RETURN(rc);
+}
+
+/* Setup _mgs fsdb and log
+ */
+int mgs__mgs_fsdb_setup(const struct lu_env *env, struct mgs_device *mgs)
+{
+ struct fs_db *fsdb = NULL;
+ int rc;
+ ENTRY;
+
+ rc = mgs_find_or_make_fsdb(env, mgs, MGSSELF_NAME, &fsdb);
+ if (!rc)
+ mgs_put_fsdb(mgs, fsdb);
+
+ RETURN(rc);
+}
+
+/* Setup params fsdb and log
+ */
+int mgs_params_fsdb_setup(const struct lu_env *env, struct mgs_device *mgs)
+{
+ struct fs_db *fsdb = NULL;
+ struct llog_handle *params_llh = NULL;
+ int rc;
+ ENTRY;
+
+ rc = mgs_find_or_make_fsdb(env, mgs, PARAMS_FILENAME, &fsdb);
+ if (!rc) {
+ mutex_lock(&fsdb->fsdb_mutex);
+ rc = record_start_log(env, mgs, ¶ms_llh, PARAMS_FILENAME);
+ if (!rc)
+ rc = record_end_log(env, ¶ms_llh);
+ mutex_unlock(&fsdb->fsdb_mutex);
+ mgs_put_fsdb(mgs, fsdb);
+ }
+
+ RETURN(rc);
+}
+
+/* Cleanup params fsdb and log
+ */
+int mgs_params_fsdb_cleanup(const struct lu_env *env, struct mgs_device *mgs)
+{
+ int rc;
+
+ rc = mgs_erase_logs(env, mgs, PARAMS_FILENAME);
+ return rc == -ENOENT ? 0 : rc;
+}
+
+/**
+ * Fill in the mgs_target_info based on data devname and param provide.
+ *
+ * @env thread context
+ * @mgs mgs device
+ * @mti mgs target info. We want to set this based other paramters
+ * passed to this function. Once setup we write it to the config
+ * logs.
+ * @devname optional OBD device name
+ * @param string that contains both what tunable to set and the value to
+ * set it to.
+ *
+ * RETURN 0 for success
+ * negative error number on failure
+ **/
+static int mgs_set_conf_param(const struct lu_env *env, struct mgs_device *mgs,
+ struct mgs_target_info *mti, const char *devname,
+ const char *param)
+{
+ struct fs_db *fsdb = NULL;
+ int dev_type;
+ int rc = 0;
+
+ ENTRY;
+ /* lustre, lustre-mdtlov, lustre-client, lustre-MDT0000 */
+ if (!devname) {
+ size_t len;
+
+ /* We have two possible cases here:
+ *
+ * 1) the device name embedded in the param:
+ * lustre-OST0000.osc.max_dirty_mb=32
+ *
+ * 2) the file system name is embedded in
+ * the param: lustre.sys.at.min=0
+ */
+ len = strcspn(param, ".=");
+ if (!len || param[len] == '=')
+ RETURN(-EINVAL);
+
+ if (len >= sizeof(mti->mti_svname))
+ RETURN(-E2BIG);
+
+ snprintf(mti->mti_svname, sizeof(mti->mti_svname),
+ "%.*s", (int)len, param);
+ param += len + 1;
+ } else {
+ if (strlcpy(mti->mti_svname, devname, sizeof(mti->mti_svname)) >=
+ sizeof(mti->mti_svname))
+ RETURN(-E2BIG);
+ }
+
+ if (!strlen(mti->mti_svname)) {
+ LCONSOLE_ERROR_MSG(0x14d, "No target specified: %s\n", param);
+ RETURN(-ENOSYS);
+ }
+
+ dev_type = mgs_parse_devname(mti->mti_svname, mti->mti_fsname,
+ &mti->mti_stripe_index);
+ switch (dev_type) {
+ /* For this case we have an invalid obd device name */
+ case -ENXIO:
+ CDEBUG(D_MGS, "%s don't contain an index\n", mti->mti_svname);
+ strlcpy(mti->mti_fsname, mti->mti_svname, MTI_NAME_MAXLEN);
+ dev_type = 0;
+ break;
+ /* Not an obd device, assume devname is the fsname.
+ * User might of only provided fsname and not obd device
+ */
+ case -EINVAL:
+ CDEBUG(D_MGS, "%s is seen as a file system name\n", mti->mti_svname);
+ strlcpy(mti->mti_fsname, mti->mti_svname, MTI_NAME_MAXLEN);
+ dev_type = 0;
+ break;
+ default:
+ if (dev_type < 0)
+ GOTO(out, rc = dev_type);
+
+ /* param related to llite isn't allowed to set by OST or MDT */
+ if (dev_type & LDD_F_SV_TYPE_OST ||
+ dev_type & LDD_F_SV_TYPE_MDT) {
+ /* param related to llite isn't allowed to set by OST
+ * or MDT
+ */
+ if (!strncmp(param, PARAM_LLITE,
+ sizeof(PARAM_LLITE) - 1))
+ GOTO(out, rc = -EINVAL);
+
+ /* Strip -osc or -mdc suffix from svname */
+ if (server_make_name(dev_type, mti->mti_stripe_index,
+ mti->mti_fsname, mti->mti_svname,
+ sizeof(mti->mti_svname)))
+ GOTO(out, rc = -EINVAL);
+ }
+ break;
+ }
+
+ if (strlcpy(mti->mti_params, param, sizeof(mti->mti_params)) >=
+ sizeof(mti->mti_params))
+ GOTO(out, rc = -E2BIG);
+
+ CDEBUG(D_MGS, "set_conf_param fs='%s' device='%s' param='%s'\n",
+ mti->mti_fsname, mti->mti_svname, mti->mti_params);
+
+ rc = mgs_find_or_make_fsdb(env, mgs, mti->mti_fsname, &fsdb);
+ if (rc)
+ GOTO(out, rc);
+
+ if (!test_bit(FSDB_MGS_SELF, &fsdb->fsdb_flags) &&
+ test_bit(FSDB_LOG_EMPTY, &fsdb->fsdb_flags)) {
+ CERROR("No filesystem targets for %s. cfg_device from lctl "
+ "is '%s'\n", mti->mti_fsname, mti->mti_svname);
+ mgs_unlink_fsdb(mgs, fsdb);
+ GOTO(out, rc = -EINVAL);
+ }
+
+ /*
+ * Revoke lock so everyone updates. Should be alright if