dsl_dir.c revision a15215608b8bd90f714f6db21ee623b584607cb6
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/dmu_objset.h>
#include <sys/dsl_dataset.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_synctask.h>
#include <sys/dsl_deleg.h>
#include <sys/metaslab.h>
#include "zfs_namecheck.h"
/* ARGSUSED */
static void
{
int t;
for (t = 0; t < TXG_SIZE; t++) {
}
/*
* The props callback list should be empty since they hold the
* dir open.
*/
}
int
{
int err;
if (err)
return (err);
#ifdef ZFS_DEBUG
{
}
#endif
if (err)
goto errout;
if (tail) {
#ifdef ZFS_DEBUG
#endif
} else {
}
if (err)
goto errout;
} else {
}
if (winner) {
} else {
}
}
/*
* The dsl_dir_t has both open-to-close and instantiate-to-evict
* holds on the spa. We need the open-to-close holds because
* otherwise the spa_refcnt wouldn't change when we open a
* dir which the spa also has open, so we could incorrectly
* the instantiate-to-evict hold because the dsl_dir_t has a
* pointer to the dd_pool, which has a pointer to the spa_t.
*/
return (0);
return (err);
}
void
{
}
/* buf must be long enough (MAXNAMELEN + strlen(MOS_DIR_NAME) + 1 should do) */
void
{
} else {
buf[0] = '\0';
}
/*
* recursive mutex so that we can use
* dprintf_dd() with dd_lock held
*/
} else {
}
}
/* Calculate name legnth, avoiding all the strcat calls of dsl_dir_name */
int
{
int result = 0;
/* parent's name + 1 for the "/" */
}
/* see dsl_dir_name */
} else {
}
return (result);
}
static int
{
char *p;
return (ENOENT);
/* This would be a good place to reserve some namespace... */
if (p && (p[1] == '/' || p[1] == '@')) {
/* two separators in a row */
return (EINVAL);
}
/*
* if the first thing is an @ or /, it had better be an
* @ and it had better not have any more ats or slashes,
* and it had better have something after the @.
*/
if (p != NULL &&
return (EINVAL);
return (ENAMETOOLONG);
p = NULL;
} else if (p[0] == '/') {
if (p-path >= MAXNAMELEN)
return (ENAMETOOLONG);
p++;
} else if (p[0] == '@') {
/*
* if the next separator is an @, there better not be
* any more slashes.
*/
return (EINVAL);
if (p-path >= MAXNAMELEN)
return (ENAMETOOLONG);
} else {
ASSERT(!"invalid p");
}
*nextp = p;
return (0);
}
/*
* same as dsl_open_dir, ignore the first component of name and use the
* spa instead
*/
int
{
char buf[MAXNAMELEN];
int err;
dsl_pool_t *dp;
if (err)
return (err);
if (err) {
return (err);
}
/* XXX this assertion belongs in spa_open */
}
if (err) {
if (openedspa)
return (err);
}
if (err)
break;
if (next[0] == '@')
break;
dprintf("looking up %s in obj%lld\n",
if (err) {
err = 0;
break;
}
if (err)
break;
}
if (err) {
if (openedspa)
return (err);
}
/*
* It's an error if there's more than one component left, or
* tailp==NULL and there's any component left.
*/
/* bad path name */
}
if (tailp)
if (openedspa)
return (err);
}
/*
* Return the dsl_dir_t, and possibly the last component which couldn't
* be found in *tail. Return NULL if the path is bogus, or if
* tail==NULL and we couldn't parse the whole name. (*tail)[0] == '@'
* means that the last component is a snapshot.
*/
int
{
}
{
if (pds) {
} else {
/* it's the root dir */
}
if (pds)
return (ddobj);
}
/* ARGSUSED */
int
{
int err;
/*
* There should be exactly two holds, both from
* dsl_dataset_destroy: one on the dd directory, and one on its
* head ds. Otherwise, someone is trying to lookup something
* inside this dir while we want to destroy it. The
* config_rwlock ensures that nobody else opens it after we
* check.
*/
return (EBUSY);
if (err)
return (err);
if (count != 0)
return (EEXIST);
return (0);
}
void
{
dd_used_t t;
/* Remove our reservation. */
val = 0;
for (t = 0; t < DD_USED_NUM; t++)
}
{
}
void
{
}
if (dsl_dir_is_clone(dd)) {
char buf[MAXNAMELEN];
}
}
void
{
/* up the hold count until we can be written out */
}
}
static int64_t
{
return (new_accounted - old_accounted);
}
void
{
/* release the hold from dsl_dir_dirty */
}
static uint64_t
{
int i;
for (i = 0; i < TXG_SIZE; i++) {
}
return (space);
}
/*
* How much space would dd have available if ancestor had delta applied
* to it? If ondiskonly is set, we're only interested in what's
* on-disk, not estimated pending changes.
*/
{
/*
* If there are no restrictions otherwise, assume we have
* unlimited space available.
*/
quota = UINT64_MAX;
}
if (!ondiskonly)
}
/*
* We have some space reserved, in addition to what our
* parent gave us.
*/
}
if (parentspace != UINT64_MAX)
parentspace -= delta;
}
/* over quota */
myspace = 0;
/*
* While it's OK to be a little over quota, if
* we think we are using more space than there
* is in the pool (which is already 1.6% more than
* dsl_pool_adjustedsize()), something is very
* wrong.
*/
} else {
/*
* the lesser of the space provided by our parent and
* the space left in our quota
*/
}
return (myspace);
}
struct tempreserve {
};
static int
{
struct tempreserve *tr;
int i;
/*
* Check against the dsl_dir's quota. We don't add in the delta
* when checking for over-quota because they get one free hit.
*/
for (i = 0; i < TXG_SIZE; i++)
/*
* On the first iteration, fetch the dataset's used-on-disk and
* refreservation values. Also, if checkrefquota is set, test if
* allocating this space would exceed the dataset's refquota.
*/
int error;
if (error) {
return (error);
}
}
/*
* If this transaction will result in a net free of space,
* we want to let it through.
*/
quota = UINT64_MAX;
else
/*
* Adjust the quota against the actual pool size at the root
* minus any outstanding deferred frees.
* To ensure that it's possible to remove files from a full
* pool without inducing transient overcommits, we throttle
* netfree transactions against a quota that is slightly larger,
* but still within the pool's allocation slop. In cases where
* we're very close to full, this will allow a steady trickle of
* removes to get through.
*/
}
}
/*
* If they are requesting more space, and our current estimate
* is over quota, they get to try again unless the actual
* on-disk is over quota and there are no pending changes (which
* may free up space for us).
*/
"quota=%lluK tr=%lluK err=%d\n",
return (retval);
}
/* We need to up our estimated delta before dropping dd_lock */
/* see if it's OK with our parent */
} else {
return (0);
}
}
/*
* Reserve space in this dsl_dir, to be used in this tx's txg.
* After the space has been dirtied (and dsl_dir_willuse_space()
* has been called), the reservation should be canceled, using
* dsl_dir_tempreserve_clear().
*/
int
{
int err;
if (asize == 0) {
*tr_cookiep = NULL;
return (0);
}
if (err == 0) {
struct tempreserve *tr;
} else {
}
}
if (err == 0) {
struct tempreserve *tr;
}
if (err)
else
*tr_cookiep = tr_list;
return (err);
}
/*
* Clear a temporary reservation that we previously made with
* dsl_dir_tempreserve_space().
*/
void
{
struct tempreserve *tr;
return;
} else {
}
}
}
static void
{
if (space > 0)
/* Make sure that we clean up dd_space_to* */
/* XXX this is potentially expensive and unnecessary... */
}
/*
* eg. when dirtying data. Be conservative (ie. OK to write less than
* this or free more than this, but don't write more or free less).
*/
void
{
}
void
{
if (needlock)
ASSERT(compressed >= 0 ||
ASSERT(uncompressed >= 0 ||
#ifdef DEBUG
dd_used_t t;
uint64_t u = 0;
for (t = 0; t < DD_USED_NUM; t++)
#endif
}
if (needlock)
}
}
void
{
return;
if (needlock)
if (needlock)
}
static int
{
int err = 0;
if (new_quota == 0)
return (0);
/*
* If we are doing the preliminary check in open context, and
* there are pending changes, then don't fail it, since the
* pending changes could under-estimate the amount of space to be
* freed up.
*/
}
return (err);
}
/* ARGSUSED */
static void
{
}
int
{
int err;
if (err)
return (err);
/*
* If someone removes a file, then tries to set the quota, we
* want to make sure the file freeing takes effect.
*/
}
return (err);
}
int
{
/*
* If we are doing the preliminary check in open context, the
* space estimates may be inaccurate.
*/
if (!dmu_tx_is_syncing(tx))
return (0);
} else {
}
return (ENOSPC);
return (ENOSPC);
}
return (0);
}
/* ARGSUSED */
static void
{
/* Roll up this additional usage into our ancestors */
}
}
int
{
int err;
if (err)
return (err);
return (err);
}
static dsl_dir_t *
{
return (dd);
}
}
return (NULL);
}
/*
* If delta is applied to dd, how much of that delta would be applied to
* ancestor? Syncing context only.
*/
static int64_t
{
return (delta);
}
struct renamearg {
const char *mynewname;
};
/*ARGSUSED*/
static int
{
int err;
/* There should be 2 references: the open and the dirty */
return (EBUSY);
/* check for existing name */
if (err == 0)
return (EEXIST);
return (err);
/* is there enough space? */
/* no rename into our descendant */
return (EINVAL);
return (err);
}
return (0);
}
static void
{
int err;
-unused_rsrv, 0, 0, tx);
unused_rsrv, 0, 0, tx);
}
}
/* remove from old parent zapobj */
/* add to new parent zapobj */
}
int
{
int err;
/* new parent should exist */
if (err)
return (err);
/* can't rename to different pool */
goto out;
}
/* new name should not already exist */
goto out;
}
out:
return (err);
}
int
{
return (ENOSPC);
return (0);
}
{
timestruc_t t;
t = dd->dd_snap_cmtime;
return (t);
}
void
{
timestruc_t t;
gethrestime(&t);
dd->dd_snap_cmtime = t;
}