File: [Development] / xfs-cmds / xfsprogs / quota / util.c (download)
Revision 1.1, Tue Apr 19 15:00:30 2005 UTC (12 years, 6 months ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
Initial version of xfs quota utility. Knows how to do user/group/project quota, provides missing freespace reporting for realtime on Linux, and for project quotas. Allows all current and future XFS quota functionality to be exposed without complicating the standard quota tools.
Merge of master-melb:xfs-cmds:22274a by kenmcd.
|
/*
* Copyright (c) 2005 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
*/
#include <pwd.h>
#include <grp.h>
#include <utmp.h>
#include "quota.h"
#define SECONDS_IN_A_DAY (24 * 60 * 60)
#define SECONDS_IN_A_HOUR (60 * 60)
#define SECONDS_IN_A_MINUTE (60)
char *
time_to_string(
__uint32_t origin,
uint flags)
{
static char timestamp[32];
time_t now, timer;
uint days, hours, minutes, seconds;
if (flags & ABSOLUTE_FLAG) {
timer = origin;
} else {
time(&now);
timer = MAX(origin - now, 0);
}
days = timer / SECONDS_IN_A_DAY;
if (days)
timer %= SECONDS_IN_A_DAY;
hours = timer / SECONDS_IN_A_HOUR;
if (hours)
timer %= SECONDS_IN_A_HOUR;
minutes = timer / SECONDS_IN_A_MINUTE;
seconds = timer % SECONDS_IN_A_MINUTE;
if (flags & LIMIT_FLAG) {
snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ?
_("[-none-]") : _("[--none--]"));
} else if (origin == 0) {
snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ?
_("[------]") : _("[--------]"));
} else if ((hours == 0 && minutes == 0 && seconds == 0) ||
(!(flags & VERBOSE_FLAG) && days > 0)) {
snprintf(timestamp, sizeof(timestamp), "[%u %s]",
days, days == 1 ? _("day") : _("days"));
} else if (flags & VERBOSE_FLAG) {
snprintf(timestamp, sizeof(timestamp), "[%u %s %02u:%02u:%02u]",
days, days == 1 ? _("day") : _("days"),
hours, minutes, seconds);
} else { /* non-verbose, less than a day remaining */
snprintf(timestamp, sizeof(timestamp),
(flags & HUMAN_FLAG) ?
"%02u:%02u:%02u" : "[%02u:%02u:%02u]",
hours, minutes, seconds);
}
return timestamp;
}
static int
round_snprintf(
char *sp,
size_t size,
const char *fmt_round,
const char *fmt_not_round,
__uint64_t value,
__uint64_t divisor)
{
double v = (double)value / divisor;
value /= divisor;
if (v == (double)value)
return snprintf(sp, size, fmt_round, (uint)value);
else
return snprintf(sp, size, fmt_not_round, v);
}
/* Basic blocks (512) bytes are returned from quotactl */
#define BBS_TO_EXABYTES(bbs) ((__uint64_t)(bbs)>>51)
#define BBS_TO_PETABYTES(bbs) ((__uint64_t)(bbs)>>41)
#define BBS_TO_TERABYTES(bbs) ((__uint64_t)(bbs)>>31)
#define BBS_TO_GIGABYTES(bbs) ((__uint64_t)(bbs)>>21)
#define BBS_TO_MEGABYTES(bbs) ((__uint64_t)(bbs)>>11)
#define BBS_TO_KILOBYTES(bbs) ((__uint64_t)(bbs)>>1)
#define BBEXABYTE ((__uint64_t)1<<51)
#define BBPETABYTE ((__uint64_t)1<<41)
#define BBTERABYTE ((__uint64_t)1<<31)
#define BBGIGABYTE ((__uint64_t)1<<21)
#define BBMEGABYTE ((__uint64_t)1<<11)
#define BBKILOBYTE ((__uint64_t)1<< 1)
char *
bbs_to_string(
__uint64_t v,
char *sp,
uint size)
{
if (v == 0)
snprintf(sp, size, "%4u", (uint)v);
else if (BBS_TO_EXABYTES(v))
round_snprintf(sp, size, "%3uE", "%3.1fE", v, BBEXABYTE);
else if (BBS_TO_PETABYTES(v))
round_snprintf(sp, size, "%3uP", "%3.1fP", v, BBPETABYTE);
else if (BBS_TO_TERABYTES(v))
round_snprintf(sp, size, "%3uT", "%3.1fT", v, BBTERABYTE);
else if (BBS_TO_GIGABYTES(v))
round_snprintf(sp, size, "%3uG", "%3.1fG", v, BBGIGABYTE);
else if (BBS_TO_MEGABYTES(v))
round_snprintf(sp, size, "%3uM", "%3.1fM", v, BBMEGABYTE);
else if (BBS_TO_KILOBYTES(v))
round_snprintf(sp, size, "%3uK", "%3.1fK", v, BBKILOBYTE);
else
snprintf(sp, size, "%4u", (uint)v << BBSHIFT); /* bytes */
return sp;
}
#define THOUSAND ((__uint64_t)1000)
#define MILLION ((__uint64_t)1000*1000)
#define BILLION ((__uint64_t)1000*1000*1000)
#define TRILLION ((__uint64_t)1000*1000*1000*1000)
#define GAZILLION ((__uint64_t)1000*1000*1000*1000*1000)
#define RIDICULOUS ((__uint64_t)1000*1000*1000*1000*1000*1000)
#define STOPALREADY ((__uint64_t)1000*1000*1000*1000*1000*1000*1000)
char *
num_to_string(
__uint64_t v,
char *sp,
uint size)
{
if (v == 0)
snprintf(sp, size, "%4u", (uint)v);
else if (v > STOPALREADY)
round_snprintf(sp, size, "%3us", "%3.1fs", v, STOPALREADY);
else if (v > RIDICULOUS)
round_snprintf(sp, size, "%3ur", "%3.1fr", v, RIDICULOUS);
else if (v > GAZILLION)
round_snprintf(sp, size, "%3ug", "%3.1fg", v, GAZILLION);
else if (v > TRILLION)
round_snprintf(sp, size, "%3ut", "%3.1ft", v, TRILLION);
else if (v > BILLION)
round_snprintf(sp, size, "%3ub", "%3.1fb", v, BILLION);
else if (v > MILLION)
round_snprintf(sp, size, "%3um", "%3.1fm", v, MILLION);
else if (v > THOUSAND)
round_snprintf(sp, size, "%3uk", "%3.1fk", v, THOUSAND);
else
snprintf(sp, size, "%4u", (uint)v);
return sp;
}
char *
pct_to_string(
__uint64_t v,
__uint64_t t,
char *sp,
uint size)
{
if (t == 0 || v == 0)
snprintf(sp, size, "%3u", (uint)0);
else if (t == v)
snprintf(sp, size, "%3u", (uint)100);
else
snprintf(sp, size, "%3u", (uint)(((double)v / t) * 100 + 1));
return sp;
}
char *
form_to_string(
uint form)
{
static char *forms[] = {
_("Blocks"), _("Inodes"), _("Realtime Blocks") };
if (form & XFS_BLOCK_QUOTA)
return forms[0];
if (form & XFS_INODE_QUOTA)
return forms[1];
if (form & XFS_RTBLOCK_QUOTA)
return forms[2];
return NULL;
}
char *
type_to_string(
uint type)
{
static char *types[] = { _("User"), _("Group"), _("Project") };
if (type & XFS_USER_QUOTA)
return types[0];
if (type & XFS_GROUP_QUOTA)
return types[1];
if (type & XFS_PROJ_QUOTA)
return types[2];
return NULL;
}
/*
* Identifier caches - user/group/project names/IDs
*/
#define NID 4096
#define IDMASK (NID-1)
typedef struct {
__uint32_t id;
char name[UT_NAMESIZE + 1];
} idcache_t;
static idcache_t uidnc[NID];
static idcache_t gidnc[NID];
static idcache_t pidnc[NID];
static int uentriesleft = NID;
static int gentriesleft = NID;
static int pentriesleft = NID;
static idcache_t *
getnextpwent(
__uint32_t id,
int byid)
{
struct passwd *pw;
static idcache_t idc;
/* /etc/passwd */
if ((pw = byid? getpwuid(id) : getpwent()) == NULL)
return NULL;
idc.id = pw->pw_uid;
strncpy(idc.name, pw->pw_name, UT_NAMESIZE);
return &idc;
}
static idcache_t *
getnextgrent(
__uint32_t id,
int byid)
{
struct group *gr;
static idcache_t idc;
if ((gr = byid? getgrgid(id) : getgrent()) == NULL)
return NULL;
idc.id = gr->gr_gid;
strncpy(idc.name, gr->gr_name, UT_NAMESIZE);
return &idc;
}
static idcache_t *
getnextprent(
__uint32_t id,
int byid)
{
fs_project_t *pr;
static idcache_t idc;
if ((pr = byid? getprprid(id) : getprent()) == NULL)
return NULL;
idc.id = pr->pr_prid;
strncpy(idc.name, pr->pr_name, UT_NAMESIZE);
return &idc;
}
char *
uid_to_name(
__uint32_t id)
{
idcache_t *ncp, *idp;
/* Check cache for name first */
ncp = &uidnc[id & IDMASK];
if (ncp->id == id && ncp->name[0])
return ncp->name;
if (uentriesleft) {
/*
* Fill this cache while seaching for a name.
* This lets us run through the file serially.
*/
if (uentriesleft == NID)
setpwent();
while (((idp = getnextpwent(id, 0)) != NULL) && uentriesleft) {
uentriesleft--;
ncp = &uidnc[idp->id & IDMASK];
if (ncp->name[0] == '\0' || idp->id == id)
memcpy(ncp, idp, sizeof(idcache_t));
if (idp->id == id)
return ncp->name;
}
endpwent();
uentriesleft = 0;
ncp = &uidnc[id & IDMASK];
}
/* Not cached - do it the slow way & insert into cache */
if ((idp = getnextpwent(id, 1)) == NULL)
return NULL;
memcpy(ncp, idp, sizeof(idcache_t));
return ncp->name;
}
char *
gid_to_name(
__uint32_t id)
{
idcache_t *ncp, *idp;
/* Check cache for name first */
ncp = &gidnc[id & IDMASK];
if (ncp->id == id && ncp->name[0])
return ncp->name;
if (gentriesleft) {
/*
* Fill this cache while seaching for a name.
* This lets us run through the file serially.
*/
if (gentriesleft == NID)
setgrent();
while (((idp = getnextgrent(id, 0)) != NULL) && gentriesleft) {
gentriesleft--;
ncp = &gidnc[idp->id & IDMASK];
if (ncp->name[0] == '\0' || idp->id == id)
memcpy(ncp, idp, sizeof(idcache_t));
if (idp->id == id)
return ncp->name;
}
endgrent();
gentriesleft = 0;
ncp = &gidnc[id & IDMASK];
}
/* Not cached - do it the slow way & insert into cache */
if ((idp = getnextgrent(id, 1)) == NULL)
return NULL;
memcpy(ncp, idp, sizeof(idcache_t));
return ncp->name;
}
char *
prid_to_name(
__uint32_t id)
{
idcache_t *ncp, *idp;
/* Check cache for name first */
ncp = &pidnc[id & IDMASK];
if (ncp->id == id && ncp->name[0])
return ncp->name;
if (pentriesleft) {
/*
* Fill this cache while seaching for a name.
* This lets us run through the file serially.
*/
if (pentriesleft == NID)
setprent();
while (((idp = getnextprent(id, 0)) != NULL) && pentriesleft) {
pentriesleft--;
ncp = &pidnc[idp->id & IDMASK];
if (ncp->name[0] == '\0' || idp->id == id)
memcpy(ncp, idp, sizeof(idcache_t));
if (idp->id == id)
return ncp->name;
}
endprent();
pentriesleft = 0;
ncp = &pidnc[id & IDMASK];
}
/* Not cached - do it the slow way & insert into cache */
if ((idp = getnextprent(id, 1)) == NULL)
return NULL;
memcpy(ncp, idp, sizeof(idcache_t));
return ncp->name;
}
/*
* Utility routine for opening an output file so that it can
* be "securely" written to (i.e. without vulnerability to a
* symlink attack).
*
* Returns NULL on failure, stdout on NULL input.
*/
FILE *
fopen_write_secure(
char *fname)
{
FILE *fp;
int fd;
if (!fname)
return stdout;
if ((fd = open(fname, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) {
fprintf(stderr, _("%s: open on %s failed: %s\n"),
progname, fname, strerror(errno));
return NULL;
}
if ((fp = fdopen(fd, "w")) == NULL) {
fprintf(stderr, _("%s: fdopen on %s failed: %s\n"),
progname, fname, strerror(errno));
close(fd);
return NULL;
}
return fp;
}