[BACK]Return to util.c CVS log [TXT][DIR] Up to [Development] / xfs-cmds / xfsprogs / quota

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;
}