Print this page
4012 Upper limit of zfs set bounds check for refreservation on volumes is too low


   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2013 by Delphix. All rights reserved.
  25  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  26  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  27  * Copyright (c) 2013 Martin Matuska. All rights reserved.
  28  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  29  */
  30 
  31 #include <ctype.h>
  32 #include <errno.h>
  33 #include <libintl.h>
  34 #include <math.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <strings.h>
  38 #include <unistd.h>
  39 #include <stddef.h>
  40 #include <zone.h>
  41 #include <fcntl.h>
  42 #include <sys/mntent.h>
  43 #include <sys/mount.h>
  44 #include <priv.h>
  45 #include <pwd.h>


1192                         }
1193 
1194                         break;
1195                 case ZFS_PROP_UTF8ONLY:
1196                         chosen_utf = (int)intval;
1197                         break;
1198                 case ZFS_PROP_NORMALIZE:
1199                         chosen_normal = (int)intval;
1200                         break;
1201                 }
1202 
1203                 /*
1204                  * For changes to existing volumes, we have some additional
1205                  * checks to enforce.
1206                  */
1207                 if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1208                         uint64_t volsize = zfs_prop_get_int(zhp,
1209                             ZFS_PROP_VOLSIZE);
1210                         uint64_t blocksize = zfs_prop_get_int(zhp,
1211                             ZFS_PROP_VOLBLOCKSIZE);

1212                         char buf[64];
1213 
1214                         switch (prop) {
1215                         case ZFS_PROP_RESERVATION:
1216                         case ZFS_PROP_REFRESERVATION:
1217                                 if (intval > volsize) {


1218                                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1219                                             "'%s' is greater than current "
1220                                             "volume size"), propname);
1221                                         (void) zfs_error(hdl, EZFS_BADPROP,
1222                                             errbuf);
1223                                         goto error;
1224                                 }
1225                                 break;
1226 
1227                         case ZFS_PROP_VOLSIZE:
1228                                 if (intval % blocksize != 0) {
1229                                         zfs_nicenum(blocksize, buf,
1230                                             sizeof (buf));
1231                                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1232                                             "'%s' must be a multiple of "
1233                                             "volume block size (%s)"),
1234                                             propname, buf);
1235                                         (void) zfs_error(hdl, EZFS_BADPROP,
1236                                             errbuf);
1237                                         goto error;


1268                     "'%s' must be set 'on' if normalization chosen"),
1269                     zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1270                 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1271                 goto error;
1272         }
1273         return (ret);
1274 
1275 error:
1276         nvlist_free(ret);
1277         return (NULL);
1278 }
1279 
1280 int
1281 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
1282 {
1283         uint64_t old_volsize;
1284         uint64_t new_volsize;
1285         uint64_t old_reservation;
1286         uint64_t new_reservation;
1287         zfs_prop_t resv_prop;
1288         nvlist_t *props;

1289 
1290         /*
1291          * If this is an existing volume, and someone is setting the volsize,
1292          * make sure that it matches the reservation, or add it if necessary.
1293          */
1294         old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
1295         if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1296                 return (-1);
1297         old_reservation = zfs_prop_get_int(zhp, resv_prop);


1298 
1299         props = fnvlist_alloc();
1300         fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
1301             zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
1302 
1303         if ((zvol_volsize_to_reservation(old_volsize, props) !=
1304             old_reservation) || nvlist_exists(nvl,
1305             zfs_prop_to_name(resv_prop))) {
1306                 fnvlist_free(props);
1307                 return (0);
1308         }
1309         if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1310             &new_volsize) != 0) {
1311                 fnvlist_free(props);
1312                 return (-1);
1313         }
1314         new_reservation = zvol_volsize_to_reservation(new_volsize, props);
1315         fnvlist_free(props);
1316 
1317         if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
1318             new_reservation) != 0) {
1319                 (void) no_memory(zhp->zfs_hdl);
1320                 return (-1);
1321         }
1322         return (1);
1323 }
1324 
1325 void
1326 zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
1327     char *errbuf)
1328 {
1329         switch (err) {
1330 
1331         case ENOSPC:
1332                 /*
1333                  * For quotas and reservations, ENOSPC indicates
1334                  * something different; setting a quota or reservation
1335                  * doesn't use any disk space.


4443                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4444                             "pool must be upgraded"));
4445                         err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4446                         break;
4447                 case EINVAL:
4448                         err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4449                         break;
4450                 case ENOENT:
4451                         err = zfs_error(hdl, EZFS_NOENT, errbuf);
4452                         break;
4453                 default:
4454                         err = zfs_standard_error_fmt(hdl, errno, errbuf);
4455                         break;
4456                 }
4457         }
4458 
4459         return (err);
4460 }
4461 
4462 /*
4463  * Convert the zvol's volume size to an appropriate reservation.

4464  * Note: If this routine is updated, it is necessary to update the ZFS test
4465  * suite's shell version in reservation.kshlib.
4466  */
4467 uint64_t
4468 zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
4469 {
4470         uint64_t numdb;
4471         uint64_t nblocks, volblocksize;
4472         int ncopies;
4473         char *strval;
4474 
4475         if (nvlist_lookup_string(props,
4476             zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
4477                 ncopies = atoi(strval);
4478         else
4479                 ncopies = 1;
4480         if (nvlist_lookup_uint64(props,
4481             zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
4482             &volblocksize) != 0)
4483                 volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
















4484         nblocks = volsize/volblocksize;
4485         /* start with metadnode L0-L6 */
4486         numdb = 7;
4487         /* calculate number of indirects */
4488         while (nblocks > 1) {
4489                 nblocks += DNODES_PER_LEVEL - 1;
4490                 nblocks /= DNODES_PER_LEVEL;
4491                 numdb += nblocks;
4492         }
4493         numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
4494         volsize *= ncopies;
4495         /*
4496          * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
4497          * compressed, but in practice they compress down to about
4498          * 1100 bytes
4499          */
4500         numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
4501         volsize += numdb;
4502         return (volsize);
4503 }


   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2013 by Delphix. All rights reserved.
  25  * Copyright 2013 DEY Storage Systems, Inc.
  26  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  27  * Copyright (c) 2013 Martin Matuska. All rights reserved.
  28  * Copyright (c) 2013 Steven Hartland. All rights reserved.
  29  */
  30 
  31 #include <ctype.h>
  32 #include <errno.h>
  33 #include <libintl.h>
  34 #include <math.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <strings.h>
  38 #include <unistd.h>
  39 #include <stddef.h>
  40 #include <zone.h>
  41 #include <fcntl.h>
  42 #include <sys/mntent.h>
  43 #include <sys/mount.h>
  44 #include <priv.h>
  45 #include <pwd.h>


1192                         }
1193 
1194                         break;
1195                 case ZFS_PROP_UTF8ONLY:
1196                         chosen_utf = (int)intval;
1197                         break;
1198                 case ZFS_PROP_NORMALIZE:
1199                         chosen_normal = (int)intval;
1200                         break;
1201                 }
1202 
1203                 /*
1204                  * For changes to existing volumes, we have some additional
1205                  * checks to enforce.
1206                  */
1207                 if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1208                         uint64_t volsize = zfs_prop_get_int(zhp,
1209                             ZFS_PROP_VOLSIZE);
1210                         uint64_t blocksize = zfs_prop_get_int(zhp,
1211                             ZFS_PROP_VOLBLOCKSIZE);
1212                         int ncopies = zfs_prop_get_int(zhp, ZFS_PROP_COPIES);
1213                         char buf[64];
1214 
1215                         switch (prop) {
1216                         case ZFS_PROP_RESERVATION:
1217                         case ZFS_PROP_REFRESERVATION:
1218                                 if (intval >
1219                                     zvol_volsize_to_reservation_impl(volsize,
1220                                     blocksize, ncopies)) {
1221                                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1222                                             "'%s' is greater than current "
1223                                             "volume size"), propname);
1224                                         (void) zfs_error(hdl, EZFS_BADPROP,
1225                                             errbuf);
1226                                         goto error;
1227                                 }
1228                                 break;
1229 
1230                         case ZFS_PROP_VOLSIZE:
1231                                 if (intval % blocksize != 0) {
1232                                         zfs_nicenum(blocksize, buf,
1233                                             sizeof (buf));
1234                                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1235                                             "'%s' must be a multiple of "
1236                                             "volume block size (%s)"),
1237                                             propname, buf);
1238                                         (void) zfs_error(hdl, EZFS_BADPROP,
1239                                             errbuf);
1240                                         goto error;


1271                     "'%s' must be set 'on' if normalization chosen"),
1272                     zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1273                 (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1274                 goto error;
1275         }
1276         return (ret);
1277 
1278 error:
1279         nvlist_free(ret);
1280         return (NULL);
1281 }
1282 
1283 int
1284 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
1285 {
1286         uint64_t old_volsize;
1287         uint64_t new_volsize;
1288         uint64_t old_reservation;
1289         uint64_t new_reservation;
1290         zfs_prop_t resv_prop;
1291         uint64_t volblocksize;
1292         int ncopies;
1293 
1294         /*
1295          * If this is an existing volume, and someone is setting the volsize,
1296          * make sure that it matches the reservation, or add it if necessary.
1297          */
1298         old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
1299         if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1300                 return (-1);
1301         old_reservation = zfs_prop_get_int(zhp, resv_prop);
1302         volblocksize = zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE);
1303         ncopies = zfs_prop_get_int(zhp, ZFS_PROP_COPIES);
1304 
1305         if ((zvol_volsize_to_reservation_impl(old_volsize, volblocksize,
1306             ncopies) != old_reservation) || nvlist_exists(nvl,
1307             zfs_prop_to_name(resv_prop)))





1308                 return (0);

1309         if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1310             &new_volsize) != 0)

1311                 return (-1);
1312         new_reservation = zvol_volsize_to_reservation_impl(new_volsize,
1313             volblocksize, ncopies);

1314 
1315         if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
1316             new_reservation) != 0) {
1317                 (void) no_memory(zhp->zfs_hdl);
1318                 return (-1);
1319         }
1320         return (1);
1321 }
1322 
1323 void
1324 zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
1325     char *errbuf)
1326 {
1327         switch (err) {
1328 
1329         case ENOSPC:
1330                 /*
1331                  * For quotas and reservations, ENOSPC indicates
1332                  * something different; setting a quota or reservation
1333                  * doesn't use any disk space.


4441                         zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4442                             "pool must be upgraded"));
4443                         err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4444                         break;
4445                 case EINVAL:
4446                         err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4447                         break;
4448                 case ENOENT:
4449                         err = zfs_error(hdl, EZFS_NOENT, errbuf);
4450                         break;
4451                 default:
4452                         err = zfs_standard_error_fmt(hdl, errno, errbuf);
4453                         break;
4454                 }
4455         }
4456 
4457         return (err);
4458 }
4459 
4460 /*
4461  * Convert the zvol's volume size to an appropriate reservation. This is a
4462  * convenience front-end to zvol_volsize_to_reservation_impl.
4463  * Note: If this routine is updated, it is necessary to update the ZFS test
4464  * suite's shell version in reservation.kshlib.
4465  */
4466 uint64_t
4467 zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
4468 {
4469         uint64_t volblocksize;

4470         int ncopies;
4471         char *strval;
4472 
4473         if (nvlist_lookup_string(props,
4474             zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
4475                 ncopies = atoi(strval);
4476         else
4477                 ncopies = 1;
4478         if (nvlist_lookup_uint64(props,
4479             zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
4480             &volblocksize) != 0)
4481                 volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
4482 
4483         return (zvol_volsize_to_reservation_impl(volsize, volblocksize,
4484             ncopies));
4485 }
4486 
4487 /*
4488  * Computes the required reservation to completely contain all blocks of a
4489  * zvol at a given volsize.
4490  */
4491 uint64_t
4492 zvol_volsize_to_reservation_impl(uint64_t volsize, uint64_t volblocksize,
4493     int ncopies)
4494 {
4495         uint64_t numdb;
4496         uint64_t nblocks;
4497 
4498         nblocks = volsize/volblocksize;
4499         /* start with metadnode L0-L6 */
4500         numdb = 7;
4501         /* calculate number of indirects */
4502         while (nblocks > 1) {
4503                 nblocks += DNODES_PER_LEVEL - 1;
4504                 nblocks /= DNODES_PER_LEVEL;
4505                 numdb += nblocks;
4506         }
4507         numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
4508         volsize *= ncopies;
4509         /*
4510          * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
4511          * compressed, but in practice they compress down to about
4512          * 1100 bytes
4513          */
4514         numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
4515         volsize += numdb;
4516         return (volsize);
4517 }