/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Pawel Jakub Dawidek . * All rights reserved. * Copyright (c) 2012, 2015, 2018 by Delphix. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2016 Nexenta Systems, Inc. All rights reserved. */ /* Portions Copyright 2010 Robert Milkowski */ #include #include #include #include #include #include #include #include int zfs_space_delta_cb(dmu_object_type_t bonustype, void *data, uint64_t *userp, uint64_t *groupp, uint64_t *projectp) { sa_hdr_phys_t sa; sa_hdr_phys_t *sap = data; uint64_t flags; int hdrsize; boolean_t swap = B_FALSE; /* * Is it a valid type of object to track? */ if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA) return (SET_ERROR(ENOENT)); /* * If we have a NULL data pointer * then assume the id's aren't changing and * return EEXIST to the dmu to let it know to * use the same ids */ if (data == NULL) return (SET_ERROR(EEXIST)); if (bonustype == DMU_OT_ZNODE) { znode_phys_t *znp = data; *userp = znp->zp_uid; *groupp = znp->zp_gid; *projectp = ZFS_DEFAULT_PROJID; return (0); } if (sap->sa_magic == 0) { /* * This should only happen for newly created files * that haven't had the znode data filled in yet. */ *userp = 0; *groupp = 0; *projectp = ZFS_DEFAULT_PROJID; return (0); } sa = *sap; if (sa.sa_magic == BSWAP_32(SA_MAGIC)) { sa.sa_magic = SA_MAGIC; sa.sa_layout_info = BSWAP_16(sa.sa_layout_info); swap = B_TRUE; } else { VERIFY3U(sa.sa_magic, ==, SA_MAGIC); } hdrsize = sa_hdrsize(&sa); VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t)); *userp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_UID_OFFSET)); *groupp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_GID_OFFSET)); flags = *((uint64_t *)((uintptr_t)data + hdrsize + SA_FLAGS_OFFSET)); if (swap) flags = BSWAP_64(flags); if (flags & ZFS_PROJID) *projectp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_PROJID_OFFSET)); else *projectp = ZFS_DEFAULT_PROJID; if (swap) { *userp = BSWAP_64(*userp); *groupp = BSWAP_64(*groupp); *projectp = BSWAP_64(*projectp); } return (0); } static void fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr, char *domainbuf, int buflen, uid_t *ridp) { uint64_t fuid; const char *domain; fuid = zfs_strtonum(fuidstr, NULL); domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid)); if (domain) (void) strlcpy(domainbuf, domain, buflen); else domainbuf[0] = '\0'; *ridp = FUID_RID(fuid); } static uint64_t zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type) { switch (type) { case ZFS_PROP_USERUSED: case ZFS_PROP_USEROBJUSED: return (DMU_USERUSED_OBJECT); case ZFS_PROP_GROUPUSED: case ZFS_PROP_GROUPOBJUSED: return (DMU_GROUPUSED_OBJECT); case ZFS_PROP_PROJECTUSED: case ZFS_PROP_PROJECTOBJUSED: return (DMU_PROJECTUSED_OBJECT); case ZFS_PROP_USERQUOTA: return (zfsvfs->z_userquota_obj); case ZFS_PROP_GROUPQUOTA: return (zfsvfs->z_groupquota_obj); case ZFS_PROP_USEROBJQUOTA: return (zfsvfs->z_userobjquota_obj); case ZFS_PROP_GROUPOBJQUOTA: return (zfsvfs->z_groupobjquota_obj); case ZFS_PROP_PROJECTQUOTA: return (zfsvfs->z_projectquota_obj); case ZFS_PROP_PROJECTOBJQUOTA: return (zfsvfs->z_projectobjquota_obj); default: return (ZFS_NO_OBJECT); } } int zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, uint64_t *cookiep, void *vbuf, uint64_t *bufsizep) { int error; zap_cursor_t zc; zap_attribute_t za; zfs_useracct_t *buf = vbuf; uint64_t obj; int offset = 0; if (!dmu_objset_userspace_present(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if ((type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTOBJQUOTA || type == ZFS_PROP_PROJECTOBJUSED) && !dmu_objset_projectquota_present(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED || type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA || type == ZFS_PROP_PROJECTOBJUSED || type == ZFS_PROP_PROJECTOBJQUOTA) && !dmu_objset_userobjspace_present(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); obj = zfs_userquota_prop_to_obj(zfsvfs, type); if (obj == ZFS_NO_OBJECT) { *bufsizep = 0; return (0); } if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED || type == ZFS_PROP_PROJECTOBJUSED) offset = DMU_OBJACCT_PREFIX_LEN; for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep); (error = zap_cursor_retrieve(&zc, &za)) == 0; zap_cursor_advance(&zc)) { if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) > *bufsizep) break; /* * skip object quota (with zap name prefix DMU_OBJACCT_PREFIX) * when dealing with block quota and vice versa. */ if ((offset > 0) != (strncmp(za.za_name, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN) == 0)) continue; fuidstr_to_sid(zfsvfs, za.za_name + offset, buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid); buf->zu_space = za.za_first_integer; buf++; } if (error == ENOENT) error = 0; ASSERT3U((uintptr_t)buf - (uintptr_t)vbuf, <=, *bufsizep); *bufsizep = (uintptr_t)buf - (uintptr_t)vbuf; *cookiep = zap_cursor_serialize(&zc); zap_cursor_fini(&zc); return (error); } int zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, const char *domain, uint64_t rid, uint64_t *valp) { char buf[20 + DMU_OBJACCT_PREFIX_LEN]; int offset = 0; int err; uint64_t obj; *valp = 0; if (!dmu_objset_userspace_present(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED || type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA || type == ZFS_PROP_PROJECTOBJUSED || type == ZFS_PROP_PROJECTOBJQUOTA) && !dmu_objset_userobjspace_present(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if (type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTOBJQUOTA || type == ZFS_PROP_PROJECTOBJUSED) { if (!dmu_objset_projectquota_present(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if (!zpl_is_valid_projid(rid)) return (SET_ERROR(EINVAL)); } obj = zfs_userquota_prop_to_obj(zfsvfs, type); if (obj == ZFS_NO_OBJECT) return (0); if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED || type == ZFS_PROP_PROJECTOBJUSED) { strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1); offset = DMU_OBJACCT_PREFIX_LEN; } err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf + offset, B_FALSE); if (err) return (err); err = zap_lookup(zfsvfs->z_os, obj, buf, 8, 1, valp); if (err == ENOENT) err = 0; return (err); } int zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, const char *domain, uint64_t rid, uint64_t quota) { char buf[32]; int err; dmu_tx_t *tx; uint64_t *objp; boolean_t fuid_dirtied; if (zfsvfs->z_version < ZPL_VERSION_USERSPACE) return (SET_ERROR(ENOTSUP)); switch (type) { case ZFS_PROP_USERQUOTA: objp = &zfsvfs->z_userquota_obj; break; case ZFS_PROP_GROUPQUOTA: objp = &zfsvfs->z_groupquota_obj; break; case ZFS_PROP_USEROBJQUOTA: objp = &zfsvfs->z_userobjquota_obj; break; case ZFS_PROP_GROUPOBJQUOTA: objp = &zfsvfs->z_groupobjquota_obj; break; case ZFS_PROP_PROJECTQUOTA: if (!dmu_objset_projectquota_enabled(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if (!zpl_is_valid_projid(rid)) return (SET_ERROR(EINVAL)); objp = &zfsvfs->z_projectquota_obj; break; case ZFS_PROP_PROJECTOBJQUOTA: if (!dmu_objset_projectquota_enabled(zfsvfs->z_os)) return (SET_ERROR(ENOTSUP)); if (!zpl_is_valid_projid(rid)) return (SET_ERROR(EINVAL)); objp = &zfsvfs->z_projectobjquota_obj; break; default: return (SET_ERROR(EINVAL)); } err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf, B_TRUE); if (err) return (err); fuid_dirtied = zfsvfs->z_fuid_dirty; tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL); if (*objp == 0) { dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE, zfs_userquota_prop_prefixes[type]); } if (fuid_dirtied) zfs_fuid_txhold(zfsvfs, tx); err = dmu_tx_assign(tx, TXG_WAIT); if (err) { dmu_tx_abort(tx); return (err); } mutex_enter(&zfsvfs->z_lock); if (*objp == 0) { *objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA, DMU_OT_NONE, 0, tx); VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, zfs_userquota_prop_prefixes[type], 8, 1, objp, tx)); } mutex_exit(&zfsvfs->z_lock); if (quota == 0) { err = zap_remove(zfsvfs->z_os, *objp, buf, tx); if (err == ENOENT) err = 0; } else { err = zap_update(zfsvfs->z_os, *objp, buf, 8, 1, "a, tx); } ASSERT(err == 0); if (fuid_dirtied) zfs_fuid_sync(zfsvfs, tx); dmu_tx_commit(tx); return (err); } boolean_t zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id) { char buf[20 + DMU_OBJACCT_PREFIX_LEN]; uint64_t used, quota, quotaobj; int err; if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) { if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os)) { dsl_pool_config_enter( dmu_objset_pool(zfsvfs->z_os), FTAG); dmu_objset_id_quota_upgrade(zfsvfs->z_os); dsl_pool_config_exit( dmu_objset_pool(zfsvfs->z_os), FTAG); } return (B_FALSE); } if (usedobj == DMU_PROJECTUSED_OBJECT) { if (!dmu_objset_projectquota_present(zfsvfs->z_os)) { if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) { dsl_pool_config_enter( dmu_objset_pool(zfsvfs->z_os), FTAG); dmu_objset_id_quota_upgrade(zfsvfs->z_os); dsl_pool_config_exit( dmu_objset_pool(zfsvfs->z_os), FTAG); } return (B_FALSE); } quotaobj = zfsvfs->z_projectobjquota_obj; } else if (usedobj == DMU_USERUSED_OBJECT) { quotaobj = zfsvfs->z_userobjquota_obj; } else if (usedobj == DMU_GROUPUSED_OBJECT) { quotaobj = zfsvfs->z_groupobjquota_obj; } else { return (B_FALSE); } if (quotaobj == 0 || zfsvfs->z_replay) return (B_FALSE); (void) sprintf(buf, "%llx", (longlong_t)id); err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, "a); if (err != 0) return (B_FALSE); (void) sprintf(buf, DMU_OBJACCT_PREFIX "%llx", (longlong_t)id); err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used); if (err != 0) return (B_FALSE); return (used >= quota); } boolean_t zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id) { char buf[20]; uint64_t used, quota, quotaobj; int err; if (usedobj == DMU_PROJECTUSED_OBJECT) { if (!dmu_objset_projectquota_present(zfsvfs->z_os)) { if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) { dsl_pool_config_enter( dmu_objset_pool(zfsvfs->z_os), FTAG); dmu_objset_id_quota_upgrade(zfsvfs->z_os); dsl_pool_config_exit( dmu_objset_pool(zfsvfs->z_os), FTAG); } return (B_FALSE); } quotaobj = zfsvfs->z_projectquota_obj; } else if (usedobj == DMU_USERUSED_OBJECT) { quotaobj = zfsvfs->z_userquota_obj; } else if (usedobj == DMU_GROUPUSED_OBJECT) { quotaobj = zfsvfs->z_groupquota_obj; } else { return (B_FALSE); } if (quotaobj == 0 || zfsvfs->z_replay) return (B_FALSE); (void) sprintf(buf, "%llx", (longlong_t)id); err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, "a); if (err != 0) return (B_FALSE); err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used); if (err != 0) return (B_FALSE); return (used >= quota); } boolean_t zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id) { return (zfs_id_overblockquota(zfsvfs, usedobj, id) || zfs_id_overobjquota(zfsvfs, usedobj, id)); } EXPORT_SYMBOL(zfs_space_delta_cb); EXPORT_SYMBOL(zfs_userspace_one); EXPORT_SYMBOL(zfs_userspace_many); EXPORT_SYMBOL(zfs_set_userquota); EXPORT_SYMBOL(zfs_id_overblockquota); EXPORT_SYMBOL(zfs_id_overobjquota); EXPORT_SYMBOL(zfs_id_overquota);