svn commit: r353037 - in head: cddl/contrib/opensolaris/cmd/zfs cddl/contrib/opensolaris/lib/libzfs/common sys/cddl/contrib/opensolaris/uts/common/fs/zfs sys/cddl/contrib/opensolaris/uts/common/fs/...

Andriy Gapon avg at FreeBSD.org
Thu Oct 3 11:08:47 UTC 2019


Author: avg
Date: Thu Oct  3 11:08:45 2019
New Revision: 353037
URL: https://svnweb.freebsd.org/changeset/base/353037

Log:
  ZFS: add bookmark renaming
  
  The feature is implemented as an extension of the existing
  ZFS_IOC_RENAME ioctl.  Both the userland and the DSL interfaces support
  renaming only a single bookmark at a time.  As of now, there is no ZCP
  interface to the new functionality.  I am going to add it once the DSL
  interface passes a test of time.
  
  This change picks up support for zfs_ioc_namecheck_t::ENTITY_NAME that
  was added to ZoL as part of Redacted Send/Receive feature by Paul
  Dagnelie <pcd at delphix.com>.  This is needed to allow a bookmark name in
  zc_name.
  
  Discussed with:	mahrens
  Reviewed by:	bcr (man page)
  Sponsored by:	CyberSecure
  Differential Revision: https://reviews.freebsd.org/D21795

Modified:
  head/cddl/contrib/opensolaris/cmd/zfs/zfs.8
  head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
  head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c

Modified: head/cddl/contrib/opensolaris/cmd/zfs/zfs.8
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zfs/zfs.8	Thu Oct  3 10:46:09 2019	(r353036)
+++ head/cddl/contrib/opensolaris/cmd/zfs/zfs.8	Thu Oct  3 11:08:45 2019	(r353037)
@@ -105,6 +105,9 @@
 .Ar snapshot snapshot
 .Nm
 .Cm rename
+.Ar bookmark bookmark
+.Nm
+.Cm rename
 .Fl u
 .Op Fl p
 .Ar filesystem filesystem
@@ -2092,6 +2095,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: head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
==============================================================================
--- head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c	Thu Oct  3 10:46:09 2019	(r353036)
+++ head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c	Thu Oct  3 11:08:45 2019	(r353037)
@@ -284,6 +284,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"));
@@ -3254,6 +3255,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.
@@ -3270,6 +3272,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 */
@@ -3320,7 +3323,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);
@@ -3332,10 +3335,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: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Thu Oct  3 10:46:09 2019	(r353036)
+++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Thu Oct  3 11:08:45 2019	(r353037)
@@ -4291,17 +4291,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';
@@ -4311,12 +4312,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));
 			}
@@ -4379,7 +4381,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;
@@ -4392,7 +4393,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) {
@@ -4437,6 +4439,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: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c	Thu Oct  3 10:46:09 2019	(r353036)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_bookmark.c	Thu Oct  3 11:08:45 2019	(r353037)
@@ -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: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h	Thu Oct  3 10:46:09 2019	(r353036)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_bookmark.h	Thu Oct  3 11:08:45 2019	(r353037)
@@ -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: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c	Thu Oct  3 10:46:09 2019	(r353036)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c	Thu Oct  3 11:08:45 2019	(r353037)
@@ -224,7 +224,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 {
@@ -922,8 +923,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
@@ -4020,8 +4034,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
@@ -4032,7 +4046,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;
 
@@ -4040,9 +4054,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, '%'))
@@ -4054,28 +4093,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)
@@ -6352,8 +6393,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,
@@ -6363,6 +6402,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,
@@ -6392,7 +6439,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);
@@ -6723,6 +6771,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:


More information about the svn-src-head mailing list