quota.c revision edea4b556f0b07459cdcc780ffcd28a40b374945
/*
* 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.
*/
/* Copyright (c) 1984, 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.
*/
/*
* Disk quota reporting program.
*/
#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <memory.h>
#include <sys/sysmacros.h>
#include <priv_utils.h>
#include <locale.h>
#include <netdb.h>
#include <zone.h>
#include <dlfcn.h>
#include <libzfs.h>
int vflag;
int nolocalquota;
extern int optind;
extern char *optarg;
#define QFNAME "quotas"
#if DEV_BSIZE < 1024
#else
#endif
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
static void zexit(int);
static int getzfsquota(char *, char *, struct dqblk *);
static void showquotas(uid_t, char *);
static void fmttime(char *, long);
static libzfs_handle_t *(*_libzfs_init)(void);
static void (*_libzfs_fini)(libzfs_handle_t *);
static void (*_zfs_close)(zfs_handle_t *);
static int (*_zfs_prop_get_userquota_int)(zfs_handle_t *, const char *,
uint64_t *);
/*
* Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
* packages. 'quota' utility supports zfs as an option.
*/
static void
load_libzfs(void)
{
void *hdl;
return;
"libzfs_init");
_zfs_prop_get_userquota_int = (int (*)())
g_zfs = _libzfs_init();
}
}
int
{
int opt;
int i;
int status = 0;
(void) textdomain(TEXT_DOMAIN);
/*
* PRIV_FILE_DAC_READ is needed to read the QFNAME file
* Clear all other privleges from the limit set, and add
* the required privilege to the bracketed set.
*/
NULL) == -1) {
gettext("Insufficient privileges, "
"quota must be set-uid root or have "
"file_dac_read privileges\n"));
exit(1);
}
load_libzfs();
switch (opt) {
case 'v':
vflag++;
break;
case 'V': /* Print command line */
{
char *opt_text;
int opt_count;
if (opt_text)
opt_text);
}
}
break;
case '?':
zexit(32);
}
}
if (vflag)
nolocalquota++;
}
zexit(0);
}
} else
}
return (status);
}
static void
{
if (uid == 0) {
if (vflag)
printf("no disk quota for uid 0\n");
return;
}
else
}
int
{
return (32);
}
if (vflag)
return (0);
}
return (0);
}
static void
{
struct failed_srv {
char *serv_name;
struct failed_srv *next;
};
int rc;
char my_zonename[ZONENAME_MAX];
zexit(32);
}
if (vflag)
continue;
if (nolocalquota ||
continue;
int count;
/*
* Skip checking quotas for file systems mounted
* in other zones. Zone names will be passed in
* following format from hasmntopt():
* "zone=<zone-name>,<mnt options...>"
*/
(my_zonename[0] != '\0')) {
continue;
}
continue;
/*
* Skip quota processing if mounted with public
* option. We are not likely to be able to pierce
* a fire wall to contact the quota server.
*/
continue;
if (count < 0)
else
"mnttab entry for %s\n",
continue;
}
/*
* We skip quota reporting on mounts with replicas
* for the following reasons:
*
* (1) Very little point in reporting quotas on
* a set of read-only replicas ... how will the
* user correct the problem?
*
* (2) Which replica would we report the quota
* for? If we pick the current replica, what
* happens when a fail over event occurs? The
* next time quota is run, the quota will look
* all different, or there won't even be one.
* This has the potential to break scripts.
*
* If we prnt quouta for all replicas, how do
* we present the output without breaking scripts?
*/
if (count > 1) {
continue;
}
/*
* Skip file systems mounted using public fh.
* We are not likely to be able to pierce
* a fire wall to contact the quota server.
*/
continue;
}
/*
* Skip getting quotas from failing servers
*/
if (failed_srv_list != NULL) {
struct failed_srv *tmp_list;
int found_failed = 0;
do {
found_failed = 1;
break;
}
if (found_failed) {
continue;
}
}
if (rc != RPC_SUCCESS) {
struct failed_srv *tmp_srv;
/*
* Failed to get quota from this server. Add
* this server to failed_srv_list and skip
* getting quotas for other mounted filesystems
* from this server.
*/
sizeof (struct failed_srv));
len * sizeof (char) + 1);
len);
}
continue;
}
} else {
continue;
}
continue;
if (vflag)
else
}
/*
* Free list of failed servers
*/
while (failed_srv_list != NULL) {
}
}
static void
{
if (dqp->dqb_bhardlimit &&
} else if (dqp->dqb_bsoftlimit &&
if (dqp->dqb_btimelimit == 0) {
printf("Over disk quota on %s, remove %luK\n",
char btimeleft[80];
printf("Over disk quota on %s, remove %luK within %s\n",
} else {
"Over disk quota on %s, time limit has expired, remove %luK\n",
}
}
if (dqp->dqb_fhardlimit &&
} else if (dqp->dqb_fsoftlimit &&
if (dqp->dqb_ftimelimit == 0) {
printf("Over file quota on %s, remove %lu file%s\n",
"s" : ""));
char ftimeleft[80];
"Over file quota on %s, remove %lu file%s within %s\n",
} else {
"Over file quota on %s, time limit has expired, remove %lu file%s\n",
"s" : ""));
}
}
}
static void
{
printf("%-12s %7s%7s%7s%12s%7s%7s%7s%12s\n",
"Filesystem",
"usage",
"quota",
"limit",
"timeleft",
"files",
"quota",
"limit",
"timeleft");
}
static void
{
char *cp;
if (dqp->dqb_btimelimit == 0) {
} else {
}
} else {
btimeleft[0] = '\0';
}
if (dqp->dqb_ftimelimit == 0) {
} else {
}
} else {
ftimeleft[0] = '\0';
}
cp = "";
} else {
}
if (dqp->dqb_curfiles == 0 &&
printf("%-12.12s %7d %6d %6d %11s %6s %6s %6s %11s\n",
cp,
"-",
"-",
"-",
"-",
"-");
} else {
printf("%-12.12s %7d %6d %6d %11s %6d %6d %6d %11s\n",
cp,
}
}
static void
{
int i;
static struct {
int c_secs; /* conversion units in secs */
char *c_str; /* unit string */
} cunits [] = {
{60*60*24*28, "months"},
{60*60*24*7, "weeks"},
{60*60*24, "days"},
{60*60, "hours"},
{60, "mins"},
{1, "secs"}
};
if (time <= 0) {
return;
}
break;
}
}
int
alldigits(char *s)
{
int c;
c = *s++;
do {
if (!isdigit(c))
return (0);
} while (c = *s++);
return (1);
}
int
{
int fd;
char qfilename[MAXPATHLEN];
return (0);
return (0);
(void) __priv_bracket(PRIV_ON);
(void) __priv_bracket(PRIV_OFF);
if (fd < 0)
return (0);
case 0: /* EOF */
/*
* Convert implicit 0 quota (EOF)
* into an explicit one (zero'ed dqblk).
*/
break;
case sizeof (struct dqblk): /* OK */
break;
default: /* ERROR */
return (0);
}
return (1);
}
int
{
int fd;
int status;
char qfile[MAXPATHLEN];
/*
* Find the mount point of any mounted file system. This is
* because the ioctl that implements the quotactl call has
* to go to a real file, and not to the block device.
*/
perror("open");
zexit(32);
}
fd = -1;
continue;
sizeof (qfile))) {
continue;
}
(void) __priv_bracket(PRIV_ON);
(void) __priv_bracket(PRIV_OFF);
if (fd != -1)
break;
}
if (fd == -1) {
return (-1);
}
} else {
return (-1);
}
(qfile)) ||
(qfile))) {
return (-1);
}
(void) __priv_bracket(PRIV_ON);
(void) __priv_bracket(PRIV_OFF);
if (fd < 0)
return (-1);
} /* else */
if (fd != 0)
return (status);
}
/*
* Return 1 if opt appears in optlist
*/
int
{
char *value;
char *opts[2];
return (0);
while (*optlist != '\0') {
return (1);
}
return (0);
}
/*
* If there are no quotas available, then getnfsquota() returns
* RPC_SYSTEMERROR to caller.
*/
static int
{
struct getquota_args gq_args;
struct getquota_rslt gq_rslt;
extern char *strchr();
int rpc_err;
if (rpc_err != RPC_SUCCESS) {
return (rpc_err);
}
case Q_OK:
{
return (RPC_SYSTEMERROR);
}
return (RPC_SUCCESS);
}
case Q_NOQUOTA:
return (RPC_SYSTEMERROR);
case Q_EPERM:
return (RPC_AUTHERROR);
default:
return (RPC_CANTDECODEARGS);
}
/* NOTREACHED */
}
int
{
static int oldprognum, oldversnum;
/*
* Cache the client handle in case there are lots
* server. If the server returns an error, don't
* make further calls.
*/
if (cl) {
}
&tottimeout);
return ((int)RPC_TIMEDOUT);
return (RPC_CANTSEND);
}
}
if (clnt_stat != RPC_SUCCESS)
return ((int)clnt_stat); /* don't bother retrying */
return ((int)clnt_stat);
}
static int
{
char propname[ZFS_MAXPROPLEN];
return (1);
return (1);
return (1);
}
return (1);
}
return (0);
}
static void
zexit(int n)
{
exit(n);
}