/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Quota system calls.
*/
#include <sys/pathname.h>
static int opendq();
static int setquota();
static int getquota();
static int quotasync();
/*
* Quota sub-system init flag.
*/
int quotas_initialized = 0;
/*
* Sys call to allow users to find out
* their current position wrt quota's
* and to allow privileged users to alter it.
*/
/*ARGSUSED*/
int
{
int error = 0;
return (EFAULT);
}
#ifdef _SYSCALL32_IMPL
else {
/* quotctl struct from ILP32 callers */
return (EFAULT);
}
#endif /* _SYSCALL32_IMPL */
}
case Q_QUOTAON:
if (quotas_initialized == 0) {
qtinit2();
quotas_initialized = 1;
}
break;
case Q_QUOTAOFF:
if (!error) {
}
break;
case Q_SETQUOTA:
case Q_SETQLIM:
break;
case Q_GETQUOTA:
cr);
break;
case Q_SYNC:
break;
case Q_ALLSYNC:
break;
default:
break;
}
return (error);
}
static int
{
/*
* wrong file system or this is the quota inode; keep looking
*/
return (0);
}
/*
* This inode is in the cache (by definition), is still valid,
* and is not a shadow inode or extended attribute directory inode,
* but does not have a quota so get the quota information.
*/
}
return (0);
}
/*
* Set the quota file up for a particular file system.
* Called as the result of a quotaon (Q_QUOTAON) ioctl.
*/
static int
{
int error;
int quotaon = 0;
return (EPERM);
/*
* Check to be sure its a regular file.
*/
return (EACCES);
}
/*
* We have vfs_dqrwlock as writer, so if quotas are disabled,
* then vfs_qinod should be NULL or we have a race somewhere.
*/
/*
* Quotas are already enabled on this file system.
*
* If the "quotas" file was replaced (different inode)
* while quotas were enabled we don't want to re-enable
* them with a new "quotas" file. Simply print a warning
* message to the console, release the new vnode, and
* return.
* XXX - The right way to fix this is to return EBUSY
* for the ioctl() issued by 'quotaon'.
*/
" Disable quotas on %s before enabling.\n",
return (0);
}
/* remove extra hold on quota file */
quotaon++;
} else {
int qlen;
/*
* Force the file to have no partially allocated blocks
* to prevent a realloc from changing the location of
* the data. We must do this even if not logging in
* case we later remount to logging.
*/
/*
* Largefiles: i_size needs to be atomically accessed now.
*/
" from quota file\n");
} else {
}
}
/*
* The file system time limits are in the dquot for uid 0.
* The time limits set the relative time the other users
* can be over quota for this file system.
* If it is zero a default is used (see quota.h).
*/
if (error == 0) {
} else if (!quotaon) {
/*
* Some sort of I/O error on the quota file, and quotas were
* not already on when we got here so clean up.
*/
ufsvfsp->vfs_qflags = 0;
}
/*
* If quotas are enabled update all valid inodes in the
* cache with quota information.
*/
}
return (error);
}
static int
{
/*
* wrong file system; keep looking
*/
return (0);
/*
* Shadow inodes and extended attribute directories
* do not have quota info records.
*/
/*
* If we have a pending logging file system quota
* transaction, then cancel it. Clear the flag to
* prevent ufs_trans_push_quota() from trying to
* deal with this transaction just in case it is
* waiting for the mutex. We decrement the counter
* since the transaction won't be needing the quota
* info record anymore.
*/
}
}
return (0);
}
/*
* Close off disk quotas for a file system.
*/
int
{
return (EPERM);
/*
* Quotas are not enabled on this file system so there is
* nothing more to do.
*/
return (0);
}
/*
* At this point, the quota subsystem is quiescent on this file
* system so we can do all the work necessary to dismantle the
* quota stuff.
*/
if (!qip)
/*
* ufs_scan_inodes() depends on vfs_qinod, so we can't
* clear it until afterwards.
*/
/*
* Sync and release the quota file inode. Since we have a private
* pointer to the quota inode and vfs_qinod is now clear we do not
* need to hold vfs_dqrwlock.
*/
return (0);
}
/*
* Private data between setquota() and setquota_scan_inode().
*/
struct setquota_data {
#define SQD_TYPE_NONE 0
int sqd_type;
};
static int
{
/*
* wrong file system; keep looking
*/
return (0);
/*
* The file system does not have quotas enabled or this is the
* file system's quota inode; keep looking.
*/
return (0);
}
/*
* This inode is in the cache (by definition), is still valid,
* is not a shadow inode or extended attribute directory inode
* and has the right uid.
*/
/*
* Transition is "no limit" to "at least one limit":
*/
}
/*
* Transition is "at least one limit" to "no limit":
*/
}
}
return (0);
}
/*
* Set various fields of the dqblk according to the command.
* Q_SETQUOTA - assign an entire dqblk structure.
* Q_SETQLIM - assign a dqblk structure except for the usage.
*/
static int
{
int error;
int contig;
return (EPERM);
/*
* Quotas are not enabled on this file system so there is
* nothing more to do.
*/
return (ESRCH);
}
/*
* At this point, the quota subsystem is quiescent on this file
* system so we can do all the work necessary to modify the quota
* information for this user.
*/
return (EFAULT);
}
if (error) {
return (error);
}
/*
* Don't change disk usage on Q_SETQLIM
*/
}
if (uid == 0) {
/*
* Timelimits for uid 0 set the relative time
* the other users can be over quota for this file system.
* If it is zero a default is used (see quota.h).
*/
} else {
if (newlim.dqb_bsoftlimit &&
if (dqp->dq_bsoftlimit == 0 ||
/* If we're suddenly over the limit(s), */
/* start the timer(s) */
(uint32_t)gethrestime_sec() +
} else {
/* If we're currently over the soft */
/* limit and were previously over the */
/* soft limit then preserve the old */
/* time limit but make sure the DQ_BLKS */
/* flag is set since we must have been */
/* previously warned. */
}
} else {
/* Either no quota or under quota, clear time limit */
newlim.dqb_btimelimit = 0;
}
if (newlim.dqb_fsoftlimit &&
if (dqp->dq_fsoftlimit == 0 ||
/* If we're suddenly over the limit(s), */
/* start the timer(s) */
(uint32_t)gethrestime_sec() +
} else {
/* If we're currently over the soft */
/* limit and were previously over the */
/* soft limit then preserve the old */
/* time limit but make sure the */
/* DQ_FILES flag is set since we must */
/* have been previously warned. */
}
} else {
/* Either no quota or under quota, clear time limit */
newlim.dqb_ftimelimit = 0;
}
}
/*
* If there was previously no limit and there is now at least
* one limit, then any inodes in the cache have NULL d_iquot
* fields (getinoquota() returns NULL when there are no limits).
*/
}
/*
* If there was previously at least one limit and there is now
* no limit, then any inodes in the cache have non-NULL d_iquot
* fields need to be reset to NULL.
*/
}
/*
* push the new quota to disk now. If this is a trans device
* then force the page out with ufs_putpage so it will be deltaed
* by ufs_startio.
*/
/*
* We must set the dq_mof even if not we are not logging in case
* we are later remount to logging.
*/
contig = 0;
} else {
}
if (scan_type) {
}
return (0);
}
/*
* Q_GETQUOTA - return current values in a dqblk structure.
*/
static int
{
int error = 0;
return (EPERM);
return (ESRCH);
}
if (error) {
return (error);
}
} else {
}
return (error);
}
/*
* Q_SYNC - sync quota files to disk.
*/
int
{
}
/*
* Sync quota information records to disk for the specified file system
* or all file systems with quotas if ufsvfsp == NULL. Grabs a reader
* lock on vfs_dqrwlock if it is needed.
*
* Currently, if ufsvfsp is NULL, then do_lock is always true, but the
* routine is coded to account for either do_lock value. This seemed
* to be the safer thing to do.
*/
int
{
if (!quotas_initialized) {
return (ESRCH);
}
/*
* The operation applies to a specific file system only.
*/
if (ufsvfsp) {
if (do_lock) {
}
/*
* Quotas are not enabled on this file system so bail.
*/
if (do_lock) {
}
return (ESRCH);
}
/*
* This operation is a no-op on a logging file system because
* quota information is treated as metadata and is in the log.
* This code path treats quota information as user data which
* is not necessary on a logging file system.
*/
if (TRANS_ISTRANS(ufsvfsp)) {
if (do_lock) {
}
return (0);
}
/*
* Try to sync all the quota info records for this
* file system:
*/
/*
* If someone else has it, then ignore it.
*/
continue;
}
/*
* The quota info record is for this file system
* and it has changes.
*/
}
}
if (do_lock) {
}
return (0);
}
/*
* Try to sync all the quota info records for *all* file systems
* for which quotas are enabled.
*/
/*
* If someone else has it, then ignore it.
*/
continue;
}
/*
* This quota info record has no changes or is
* not a valid quota info record yet.
*/
continue;
}
/*
* Now we have a potential lock order problem:
*
* vfs_dqrwlock > dq_lock
*
* so if we have to get vfs_dqrwlock, then go thru hoops
* to avoid deadlock. If we cannot get the order right,
* then we ignore this quota info record.
*/
if (do_lock) {
/*
* If we can't grab vfs_dqrwlock, then we don't
* want to wait to avoid deadlock.
*/
RW_READER) == 0) {
continue;
}
/*
* Okay, now we have both dq_lock and vfs_dqrwlock.
* We should not deadlock for the following reasons:
* - If another thread has a reader lock on
* vfs_dqrwlock and is waiting for dq_lock,
* there is no conflict because we can also have
* a reader lock on vfs_dqrwlock.
* - If another thread has a writer lock on
* vfs_dqrwlock and is waiting for dq_lock,
* we would have failed the rw_tryenter() above
* and given up dq_lock.
* - Since we have dq_lock another thread cannot
* have it and be waiting for vfs_dqrwlock.
*/
}
/*
* Since we got to this file system via a quota info
* record and we have vfs_dqrwlock this is paranoia
* to make sure that quotas are enabled.
*/
/*
* We are not logging. See above logging file system
* comment.
*/
if (!TRANS_ISTRANS(ufsvfsp)) {
}
/*
* Since we have a private copy of dqp->dq_ufsvfsp,
* we can drop dq_lock now.
*/
if (do_lock) {
}
}
return (0);
}