Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 19 Oct 2019 08:19:49 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r353759 - in stable/11: cddl/contrib/opensolaris/cmd/zfs cddl/contrib/opensolaris/lib/libzfs/common sys/cddl/contrib/opensolaris/uts/common/fs/zfs sys/cddl/contrib/opensolaris/uts/commo...
Message-ID:  <201910190819.x9J8JnWB095690@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Sat Oct 19 08:19:49 2019
New Revision: 353759
URL: https://svnweb.freebsd.org/changeset/base/353759

Log:
  MFC r353037: ZFS: add bookmark renaming

Modified:
  stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8
  stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
  stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
  stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
  stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h
  stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8
==============================================================================
--- stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8	Sat Oct 19 08:19:31 2019	(r353758)
+++ stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs.8	Sat Oct 19 08:19:49 2019	(r353759)
@@ -104,6 +104,9 @@
 .Ar snapshot snapshot
 .Nm
 .Cm rename
+.Ar bookmark bookmark
+.Nm
+.Cm rename
 .Fl u
 .Op Fl p
 .Ar filesystem filesystem
@@ -2086,6 +2089,16 @@ flag.
 .Pp
 Recursively rename the snapshots of all descendent datasets. Snapshots are the
 only dataset that can be renamed recursively.
+.It Xo
+.Nm
+.Cm rename
+.Ar bookmark bookmark
+.Xc
+.Pp
+Renames the given bookmark.
+Bookmarks can only be renamed within the parent file system or volume.
+When renaming a bookmark, the parent file system or volume of the bookmark
+does not need to be specified as part of the second argument.
 .It Xo
 .Nm
 .Cm list

Modified: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
==============================================================================
--- stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c	Sat Oct 19 08:19:31 2019	(r353758)
+++ stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c	Sat Oct 19 08:19:49 2019	(r353759)
@@ -283,6 +283,7 @@ get_usage(zfs_help_t idx)
 		    "<filesystem|volume|snapshot>\n"
 		    "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
 		    "\trename -r <snapshot> <snapshot>\n"
+		    "\trename <bookmark> <bookmark>\n"
 		    "\trename -u [-p] <filesystem> <filesystem>"));
 	case HELP_ROLLBACK:
 		return (gettext("\trollback [-rRf] <snapshot>\n"));
@@ -3253,6 +3254,7 @@ zfs_do_list(int argc, char **argv)
  * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
  * zfs rename [-f] -p <fs | vol> <fs | vol>
  * zfs rename -r <snap> <snap>
+ * zfs rename <bmark> <bmark>
  * zfs rename -u [-p] <fs> <fs>
  *
  * Renames the given dataset to another of the same type.
@@ -3269,6 +3271,7 @@ zfs_do_rename(int argc, char **argv)
 	int ret = 0;
 	int types;
 	boolean_t parents = B_FALSE;
+	boolean_t bookmarks = B_FALSE;
 	char *snapshot = NULL;
 
 	/* check options */
@@ -3319,7 +3322,7 @@ zfs_do_rename(int argc, char **argv)
 		usage(B_FALSE);
 	}
 
-	if (flags.recurse && strchr(argv[0], '@') == 0) {
+	if (flags.recurse && strchr(argv[0], '@') == NULL) {
 		(void) fprintf(stderr, gettext("source dataset for recursive "
 		    "rename must be a snapshot\n"));
 		usage(B_FALSE);
@@ -3331,10 +3334,22 @@ zfs_do_rename(int argc, char **argv)
 		usage(B_FALSE);
 	}
 
+	if (strchr(argv[0], '#') != NULL)
+		bookmarks = B_TRUE;
+
+	if (bookmarks && (flags.nounmount || flags.recurse ||
+	    flags.forceunmount || parents)) {
+		(void) fprintf(stderr, gettext("options are not supported "
+		    "for renaming bookmarks\n"));
+		usage(B_FALSE);
+	}
+
 	if (flags.nounmount)
 		types = ZFS_TYPE_FILESYSTEM;
 	else if (parents)
 		types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
+	else if (bookmarks)
+		types = ZFS_TYPE_BOOKMARK;
 	else
 		types = ZFS_TYPE_DATASET;
 

Modified: stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
==============================================================================
--- stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Sat Oct 19 08:19:31 2019	(r353758)
+++ stable/11/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Sat Oct 19 08:19:49 2019	(r353759)
@@ -4286,17 +4286,18 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
 	/*
 	 * Make sure the target name is valid
 	 */
-	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
-		if ((strchr(target, '@') == NULL) ||
-		    *target == '@') {
+	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
+	    zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
+		const char sep = zhp->zfs_type == ZFS_TYPE_SNAPSHOT ? '@' : '#';
+
+		if ((strchr(target, sep) == NULL) || *target == sep) {
 			/*
 			 * Snapshot target name is abbreviated,
 			 * reconstruct full dataset name
 			 */
-			(void) strlcpy(parent, zhp->zfs_name,
-			    sizeof (parent));
-			delim = strchr(parent, '@');
-			if (strchr(target, '@') == NULL)
+			(void) strlcpy(parent, zhp->zfs_name, sizeof (parent));
+			delim = strchr(parent, sep);
+			if (strchr(target, sep) == NULL)
 				*(++delim) = '\0';
 			else
 				*delim = '\0';
@@ -4306,12 +4307,13 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
 			/*
 			 * Make sure we're renaming within the same dataset.
 			 */
-			delim = strchr(target, '@');
+			delim = strchr(target, sep);
 			if (strncmp(zhp->zfs_name, target, delim - target)
-			    != 0 || zhp->zfs_name[delim - target] != '@') {
+			    != 0 || zhp->zfs_name[delim - target] != sep) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "snapshots must be part of same "
-				    "dataset"));
+				    "%s must be part of same dataset"),
+				    zhp->zfs_type == ZFS_TYPE_SNAPSHOT ?
+				    "snapshots" : "bookmarks");
 				return (zfs_error(hdl, EZFS_CROSSTARGET,
 				    errbuf));
 			}
@@ -4374,7 +4376,6 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
 		flags.nounmount = B_TRUE;
 	}
 	if (flags.recurse) {
-
 		parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
 		if (parentname == NULL) {
 			ret = -1;
@@ -4387,7 +4388,8 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
 			ret = -1;
 			goto error;
 		}
-	} else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
+	} else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT &&
+	    zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
 		if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
 		    flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0,
 		    flags.forceunmount ? MS_FORCE : 0)) == NULL) {
@@ -4432,6 +4434,8 @@ zfs_rename(zfs_handle_t *zhp, const char *source, cons
 			    "a child dataset already has a snapshot "
 			    "with the new name"));
 			(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
+		} else if (errno == EINVAL) {
+			(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 		} else {
 			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
 		}

Modified: stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
==============================================================================
--- stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c	Sat Oct 19 08:19:31 2019	(r353758)
+++ stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c	Sat Oct 19 08:19:49 2019	(r353759)
@@ -459,3 +459,108 @@ dsl_bookmark_destroy(nvlist_t *bmarks, nvlist_t *error
 	fnvlist_free(dbda.dbda_success);
 	return (rv);
 }
+
+typedef struct dsl_bookmark_rename_arg {
+	const char *dbra_fsname;
+	const char *dbra_oldname;
+	const char *dbra_newname;
+} dsl_bookmark_rename_arg_t;
+
+static int
+dsl_bookmark_rename_check(void *arg, dmu_tx_t *tx)
+{
+	dsl_bookmark_rename_arg_t *dbra = arg;
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	dsl_dataset_t *ds;
+	zfs_bookmark_phys_t bmark_phys;
+	int error;
+
+	if (!spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS))
+		return (SET_ERROR(ENOTSUP));
+
+	/* Check validity and the full length of the new bookmark name. */
+	if (zfs_component_namecheck(dbra->dbra_newname, NULL, NULL))
+		return (SET_ERROR(EINVAL));
+	if (strlen(dbra->dbra_fsname) + strlen(dbra->dbra_newname) + 1 >=
+	    ZFS_MAX_DATASET_NAME_LEN)
+		return (SET_ERROR(ENAMETOOLONG));
+
+	error = dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds);
+	if (error != 0)
+		return (error);
+	if (ds->ds_is_snapshot) {
+		dsl_dataset_rele(ds, FTAG);
+		return (SET_ERROR(EINVAL));
+	}
+	error = dsl_dataset_bmark_lookup(ds, dbra->dbra_oldname, &bmark_phys);
+	if (error != 0) {
+		dsl_dataset_rele(ds, FTAG);
+		return (error);
+	}
+
+	error = dsl_dataset_bmark_lookup(ds, dbra->dbra_newname, &bmark_phys);
+	dsl_dataset_rele(ds, FTAG);
+	if (error == 0)
+		return (SET_ERROR(EEXIST));
+	if (error != ESRCH)
+		return (error);
+	return (0);
+}
+
+static void
+dsl_bookmark_rename_sync(void *arg, dmu_tx_t *tx)
+{
+	zfs_bookmark_phys_t bmark_phys;
+	dsl_bookmark_rename_arg_t *dbra = arg;
+	dsl_pool_t *dp = dmu_tx_pool(tx);
+	objset_t *mos;
+	dsl_dataset_t *ds;
+	uint64_t bmark_zapobj;
+	uint64_t int_size, num_ints;
+	matchtype_t mt = 0;
+	int error;
+
+	ASSERT(spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_BOOKMARKS));
+	VERIFY0(dsl_dataset_hold(dp, dbra->dbra_fsname, FTAG, &ds));
+
+	mos = ds->ds_dir->dd_pool->dp_meta_objset;
+	bmark_zapobj = ds->ds_bookmarks;
+
+	if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
+		mt = MT_NORMALIZE;
+
+	VERIFY0(zap_length(mos, bmark_zapobj, dbra->dbra_oldname,
+	    &int_size, &num_ints));
+	ASSERT3U(int_size, ==, sizeof (uint64_t));
+	VERIFY0(zap_lookup_norm(mos, bmark_zapobj, dbra->dbra_oldname, int_size,
+	    num_ints, &bmark_phys, mt, NULL, 0, NULL));
+	VERIFY0(zap_remove_norm(mos, bmark_zapobj, dbra->dbra_oldname, mt, tx));
+
+	VERIFY0(zap_add(mos, bmark_zapobj, dbra->dbra_newname, int_size,
+	    num_ints, &bmark_phys, tx));
+
+	spa_history_log_internal_ds(ds, "rename bookmark", tx,
+	    "#%s -> #%s creation_txg=%llu",
+	    dbra->dbra_oldname, dbra->dbra_newname,
+	    (longlong_t)bmark_phys.zbm_creation_txg);
+
+	dsl_dataset_rele(ds, FTAG);
+}
+
+/*
+ * The bookmarks must all be in the same pool.
+ */
+int
+dsl_bookmark_rename(const char *fsname, const char *oldbmark,
+    const char *newbmark)
+{
+	dsl_bookmark_rename_arg_t dbra;
+
+	dbra.dbra_fsname = fsname;
+	dbra.dbra_oldname = oldbmark;
+	dbra.dbra_newname = newbmark;
+
+	return (dsl_sync_task(fsname, dsl_bookmark_rename_check,
+	    dsl_bookmark_rename_sync, &dbra, 1, ZFS_SPACE_CHECK_NORMAL));
+}
+

Modified: stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h
==============================================================================
--- stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h	Sat Oct 19 08:19:31 2019	(r353758)
+++ stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h	Sat Oct 19 08:19:49 2019	(r353759)
@@ -41,6 +41,7 @@ int dsl_bookmark_create(nvlist_t *, nvlist_t *);
 int dsl_get_bookmarks(const char *, nvlist_t *, nvlist_t *);
 int dsl_get_bookmarks_impl(dsl_dataset_t *, nvlist_t *, nvlist_t *);
 int dsl_bookmark_destroy(nvlist_t *, nvlist_t *);
+int dsl_bookmark_rename(const char *fs, const char *from, const char *to);
 int dsl_bookmark_lookup(struct dsl_pool *, const char *,
     struct dsl_dataset *, zfs_bookmark_phys_t *);
 

Modified: stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c	Sat Oct 19 08:19:31 2019	(r353758)
+++ stable/11/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c	Sat Oct 19 08:19:49 2019	(r353759)
@@ -225,7 +225,8 @@ typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t
 typedef enum {
 	NO_NAME,
 	POOL_NAME,
-	DATASET_NAME
+	DATASET_NAME,
+	ENTITY_NAME
 } zfs_ioc_namecheck_t;
 
 typedef enum {
@@ -923,8 +924,21 @@ static int
 zfs_secpolicy_rename(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 {
 	char *at = NULL;
+	char *pound;
 	int error;
 
+	if ((pound = strchr(zc->zc_name, '#')) != NULL) {
+		*pound = '\0';
+		error = zfs_secpolicy_write_perms(zc->zc_name,
+		    ZFS_DELEG_PERM_RENAME, cr);
+		if (error == 0) {
+			error = zfs_secpolicy_write_perms(zc->zc_name,
+			    ZFS_DELEG_PERM_BOOKMARK, cr);
+		}
+		*pound = '#';
+		return (error);
+	}
+
 	if ((zc->zc_cookie & 1) != 0) {
 		/*
 		 * This is recursive rename, so the starting snapshot might
@@ -4021,8 +4035,8 @@ recursive_unmount(const char *fsname, void *arg)
 
 /*
  * inputs:
- * zc_name	old name of dataset
- * zc_value	new name of dataset
+ * zc_name	old name of dataset or bookmark
+ * zc_value	new name of dataset or bookmark
  * zc_cookie	recursive flag (only valid for snapshots)
  *
  * outputs:	none
@@ -4033,7 +4047,7 @@ zfs_ioc_rename(zfs_cmd_t *zc)
 	objset_t *os;
 	dmu_objset_type_t ost;
 	boolean_t recursive = zc->zc_cookie & 1;
-	char *at;
+	char *pos, *pos2;
 	boolean_t allow_mounted = B_TRUE;
 	int err;
 
@@ -4041,9 +4055,34 @@ zfs_ioc_rename(zfs_cmd_t *zc)
 	allow_mounted = (zc->zc_cookie & 2) != 0;
 #endif
 
-	/* "zfs rename" from and to ...%recv datasets should both fail */
 	zc->zc_name[sizeof (zc->zc_name) - 1] = '\0';
 	zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
+
+	pos = strchr(zc->zc_name, '#');
+	if (pos != NULL) {
+		/* Bookmarks must be in same fs. */
+		pos2 = strchr(zc->zc_value, '#');
+		if (pos2 == NULL)
+			return (SET_ERROR(EINVAL));
+
+		/* Recursive flag is not supported yet. */
+		if (recursive)
+			return (SET_ERROR(ENOTSUP));
+
+		*pos = '\0';
+		*pos2 = '\0';
+		if (strcmp(zc->zc_name, zc->zc_value) == 0) {
+			err = dsl_bookmark_rename(zc->zc_name,
+			    pos + 1, pos2 + 1);
+		} else {
+			err = SET_ERROR(EXDEV);
+		}
+		*pos = '#';
+		*pos2 = '#';
+		return (err);
+	}
+
+	/* "zfs rename" from and to ...%recv datasets should both fail */
 	if (dataset_namecheck(zc->zc_name, NULL, NULL) != 0 ||
 	    dataset_namecheck(zc->zc_value, NULL, NULL) != 0 ||
 	    strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%'))
@@ -4055,28 +4094,30 @@ zfs_ioc_rename(zfs_cmd_t *zc)
 	ost = dmu_objset_type(os);
 	dmu_objset_rele(os, FTAG);
 
-	at = strchr(zc->zc_name, '@');
-	if (at != NULL) {
-		/* snaps must be in same fs */
-		int error;
-
-		if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1))
-			return (SET_ERROR(EXDEV));
-		*at = '\0';
-		if (ost == DMU_OST_ZFS && !allow_mounted) {
-			error = dmu_objset_find(zc->zc_name,
-			    recursive_unmount, at + 1,
-			    recursive ? DS_FIND_CHILDREN : 0);
-			if (error != 0) {
-				*at = '@';
-				return (error);
+	pos = strchr(zc->zc_name, '@');
+	if (pos != NULL) {
+		/* Snapshots must be in same fs. */
+		pos2 = strchr(zc->zc_value, '@');
+		if (pos2 == NULL)
+			return (SET_ERROR(EINVAL));
+		*pos = '\0';
+		*pos2 = '\0';
+		if (strcmp(zc->zc_name, zc->zc_value) != 0) {
+			err = SET_ERROR(EXDEV);
+		} else {
+			if (ost == DMU_OST_ZFS && !allow_mounted) {
+				err = dmu_objset_find(zc->zc_name,
+				    recursive_unmount, pos + 1,
+				    recursive ? DS_FIND_CHILDREN : 0);
 			}
+			if (err == 0) {
+				err = dsl_dataset_rename_snapshot(zc->zc_name,
+				    pos + 1, pos2 + 1, recursive);
+			}
 		}
-		error = dsl_dataset_rename_snapshot(zc->zc_name,
-		    at + 1, strchr(zc->zc_value, '@') + 1, recursive);
-		*at = '@';
-
-		return (error);
+		*pos = '@';
+		*pos2 = '@';
+		return (err);
 	} else {
 #ifdef illumos
 		if (ost == DMU_OST_ZVOL)
@@ -6341,8 +6382,6 @@ zfs_ioctl_init(void)
 	    zfs_secpolicy_none);
 	zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy,
 	    zfs_secpolicy_destroy);
-	zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename,
-	    zfs_secpolicy_rename);
 	zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv,
 	    zfs_secpolicy_recv);
 	zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote,
@@ -6352,6 +6391,14 @@ zfs_ioctl_init(void)
 	zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl,
 	    zfs_secpolicy_set_fsacl);
 
+	/*
+	 * Not using zfs_ioctl_register_dataset_modify as DATASET_NAME check
+	 * won't allow a bookmark name.
+	 */
+	zfs_ioctl_register_legacy(ZFS_IOC_RENAME, zfs_ioc_rename,
+	    zfs_secpolicy_rename, ENTITY_NAME, B_TRUE,
+	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
+
 	zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share,
 	    zfs_secpolicy_share, POOL_CHECK_NONE);
 	zfs_ioctl_register_dataset_nolog(ZFS_IOC_SMB_ACL, zfs_ioc_smb_acl,
@@ -6381,7 +6428,8 @@ pool_status_check(const char *name, zfs_ioc_namecheck_
 	spa_t *spa;
 	int error;
 
-	ASSERT(type == POOL_NAME || type == DATASET_NAME);
+	ASSERT(type == POOL_NAME || type == DATASET_NAME ||
+	    type == ENTITY_NAME);
 
 	if (check & POOL_CHECK_NONE)
 		return (0);
@@ -6712,6 +6760,15 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t ar
 		else
 			error = pool_status_check(zc->zc_name,
 			    vec->zvec_namecheck, vec->zvec_pool_check);
+		break;
+
+	case ENTITY_NAME:
+		if (entity_namecheck(zc->zc_name, NULL, NULL) != 0) {
+			error = SET_ERROR(EINVAL);
+		} else {
+			error = pool_status_check(zc->zc_name,
+			    vec->zvec_namecheck, vec->zvec_pool_check);
+		}
 		break;
 
 	case NO_NAME:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201910190819.x9J8JnWB095690>