changeset 3978:2dd668007b7a

6533813 recursive snapshotting resulted in a bad stack overflow
author mmusante
date Thu, 05 Apr 2007 20:34:28 -0700
parents 4d8c8b4553a3
children 4fa53442c9a8
files usr/src/lib/libzfs/common/libzfs.h usr/src/lib/libzfs/common/libzfs_util.c usr/src/uts/common/fs/zfs/dmu_objset.c usr/src/uts/common/fs/zfs/dsl_dataset.c usr/src/uts/common/fs/zfs/dsl_dir.c usr/src/uts/common/fs/zfs/sys/dsl_dir.h
diffstat 6 files changed, 79 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/libzfs/common/libzfs.h	Thu Apr 05 15:35:55 2007 -0700
+++ b/usr/src/lib/libzfs/common/libzfs.h	Thu Apr 05 20:34:28 2007 -0700
@@ -97,6 +97,7 @@
 	EZFS_POOLPROPS,		/* couldn't retrieve pool props */
 	EZFS_POOL_NOTSUP,	/* ops not supported for this type of pool */
 	EZFS_POOL_INVALARG,	/* invalid argument for this pool operation */
+	EZFS_NAMETOOLONG,	/* dataset name is too long */
 	EZFS_UNKNOWN
 };
 
--- a/usr/src/lib/libzfs/common/libzfs_util.c	Thu Apr 05 15:35:55 2007 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_util.c	Thu Apr 05 20:34:28 2007 -0700
@@ -167,6 +167,8 @@
 	case EZFS_POOL_INVALARG:
 		return (dgettext(TEXT_DOMAIN, "invalid argument for "
 		    "this pool operation"));
+	case EZFS_NAMETOOLONG:
+		return (dgettext(TEXT_DOMAIN, "dataset name is too long"));
 	case EZFS_UNKNOWN:
 		return (dgettext(TEXT_DOMAIN, "unknown error"));
 	default:
@@ -306,6 +308,11 @@
 		    "dataset is busy"));
 		zfs_verror(hdl, EZFS_BUSY, fmt, ap);
 		break;
+
+	case ENAMETOOLONG:
+		zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
+		break;
+
 	default:
 		zfs_error_aux(hdl, strerror(errno));
 		zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c	Thu Apr 05 15:35:55 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c	Thu Apr 05 20:34:28 2007 -0700
@@ -948,7 +948,7 @@
 	objset_t *os;
 	uint64_t snapobj;
 	zap_cursor_t zc;
-	zap_attribute_t attr;
+	zap_attribute_t *attr;
 	char *child;
 	int do_self, err;
 
@@ -958,6 +958,7 @@
 
 	/* NB: the $MOS dir doesn't have a head dataset */
 	do_self = (dd->dd_phys->dd_head_dataset_obj != 0);
+	attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP);
 
 	/*
 	 * Iterate over all children.
@@ -965,10 +966,10 @@
 	if (flags & DS_FIND_CHILDREN) {
 		for (zap_cursor_init(&zc, dd->dd_pool->dp_meta_objset,
 		    dd->dd_phys->dd_child_dir_zapobj);
-		    zap_cursor_retrieve(&zc, &attr) == 0;
+		    zap_cursor_retrieve(&zc, attr) == 0;
 		    (void) zap_cursor_advance(&zc)) {
-			ASSERT(attr.za_integer_length == sizeof (uint64_t));
-			ASSERT(attr.za_num_integers == 1);
+			ASSERT(attr->za_integer_length == sizeof (uint64_t));
+			ASSERT(attr->za_num_integers == 1);
 
 			/*
 			 * No separating '/' because parent's name ends in /.
@@ -977,7 +978,7 @@
 			/* XXX could probably just use name here */
 			dsl_dir_name(dd, child);
 			(void) strcat(child, "/");
-			(void) strcat(child, attr.za_name);
+			(void) strcat(child, attr->za_name);
 			err = dmu_objset_find(child, func, arg, flags);
 			kmem_free(child, MAXPATHLEN);
 			if (err)
@@ -987,6 +988,7 @@
 
 		if (err) {
 			dsl_dir_close(dd, FTAG);
+			kmem_free(attr, sizeof (zap_attribute_t));
 			return (err);
 		}
 	}
@@ -1002,16 +1004,16 @@
 		dmu_objset_close(os);
 
 		for (zap_cursor_init(&zc, dd->dd_pool->dp_meta_objset, snapobj);
-		    zap_cursor_retrieve(&zc, &attr) == 0;
+		    zap_cursor_retrieve(&zc, attr) == 0;
 		    (void) zap_cursor_advance(&zc)) {
-			ASSERT(attr.za_integer_length == sizeof (uint64_t));
-			ASSERT(attr.za_num_integers == 1);
+			ASSERT(attr->za_integer_length == sizeof (uint64_t));
+			ASSERT(attr->za_num_integers == 1);
 
 			child = kmem_alloc(MAXPATHLEN, KM_SLEEP);
 			/* XXX could probably just use name here */
 			dsl_dir_name(dd, child);
 			(void) strcat(child, "@");
-			(void) strcat(child, attr.za_name);
+			(void) strcat(child, attr->za_name);
 			err = func(child, arg);
 			kmem_free(child, MAXPATHLEN);
 			if (err)
@@ -1021,6 +1023,7 @@
 	}
 
 	dsl_dir_close(dd, FTAG);
+	kmem_free(attr, sizeof (zap_attribute_t));
 
 	if (err)
 		return (err);
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c	Thu Apr 05 15:35:55 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c	Thu Apr 05 20:34:28 2007 -0700
@@ -491,6 +491,32 @@
 	}
 }
 
+static int
+dsl_dataset_namelen(dsl_dataset_t *ds)
+{
+	int result;
+
+	if (ds == NULL) {
+		result = 3;	/* "mos" */
+	} else {
+		result = dsl_dir_namelen(ds->ds_dir);
+		VERIFY(0 == dsl_dataset_get_snapname(ds));
+		if (ds->ds_snapname[0]) {
+			++result;	/* adding one for the @-sign */
+			if (!MUTEX_HELD(&ds->ds_lock)) {
+				/* see dsl_datset_name */
+				mutex_enter(&ds->ds_lock);
+				result += strlen(ds->ds_snapname);
+				mutex_exit(&ds->ds_lock);
+			} else {
+				result += strlen(ds->ds_snapname);
+			}
+		}
+	}
+
+	return (result);
+}
+
 void
 dsl_dataset_close(dsl_dataset_t *ds, int mode, void *tag)
 {
@@ -1328,6 +1354,13 @@
 	if (err != ENOENT)
 		return (err);
 
+	/*
+	 * Check that the dataset's name is not too long.  Name consists
+	 * of the dataset's length + 1 for the @-sign + snapshot name's length
+	 */
+	if (dsl_dataset_namelen(ds) + 1 + strlen(snapname) >= MAXNAMELEN)
+		return (ENAMETOOLONG);
+
 	ds->ds_trysnap_txg = tx->tx_txg;
 	return (0);
 }
--- a/usr/src/uts/common/fs/zfs/dsl_dir.c	Thu Apr 05 15:35:55 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/dsl_dir.c	Thu Apr 05 20:34:28 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -207,6 +207,29 @@
 	}
 }
 
+/* Calculate name legnth, avoiding all the strcat calls of dsl_dir_name */
+int
+dsl_dir_namelen(dsl_dir_t *dd)
+{
+	int result = 0;
+
+	if (dd->dd_parent) {
+		/* parent's name + 1 for the "/" */
+		result = dsl_dir_namelen(dd->dd_parent) + 1;
+	}
+
+	if (!MUTEX_HELD(&dd->dd_lock)) {
+		/* see dsl_dir_name */
+		mutex_enter(&dd->dd_lock);
+		result += strlen(dd->dd_myname);
+		mutex_exit(&dd->dd_lock);
+	} else {
+		result += strlen(dd->dd_myname);
+	}
+
+	return (result);
+}
+
 int
 dsl_dir_is_private(dsl_dir_t *dd)
 {
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dir.h	Thu Apr 05 15:35:55 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dir.h	Thu Apr 05 20:34:28 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -97,6 +97,7 @@
 int dsl_dir_open_obj(dsl_pool_t *dp, uint64_t ddobj,
     const char *tail, void *tag, dsl_dir_t **);
 void dsl_dir_name(dsl_dir_t *dd, char *buf);
+int dsl_dir_namelen(dsl_dir_t *dd);
 int dsl_dir_is_private(dsl_dir_t *dd);
 uint64_t dsl_dir_create_sync(dsl_dir_t *pds, const char *name, dmu_tx_t *tx);
 void dsl_dir_create_root(objset_t *mos, uint64_t *ddobjp, dmu_tx_t *tx);