blob: 50470aba7b05bc1d2fb40d55bf58e36b950937f6 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include <sys/types.h>
#include <stdbool.h>
#include <pwd.h>
#include <grp.h>
#include <utmp.h>
#include "init.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(
time_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);
}
/*
* If we are in verbose mode, or if less than a day remains, we
* will show "X days hh:mm:ss" so the user knows the exact timer status.
*
* Otherwise, we round down to the nearest day - so we add 30s here
* such that setting and reporting a limit in rapid succession will
* show the limit which was just set, rather than immediately reporting
* one day less.
*/
if ((timer > SECONDS_IN_A_DAY) && !(flags & VERBOSE_FLAG))
timer += 30; /* seconds */
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 portion,
uint64_t whole,
char *buf,
uint size)
{
uint percent;
percent = whole ? (uint) (100.0 * portion / whole + 0.5) : 0;
if (snprintf(buf, size, "%3u", percent) < 0)
return "???";
return buf;
}
char *
form_to_string(
uint form)
{
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)
{
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[NMAX+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, NMAX);
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, NMAX);
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, NMAX);
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) {
exitcode = 1;
fprintf(stderr, _("%s: open on %s failed: %s\n"),
progname, fname, strerror(errno));
return NULL;
}
if ((fp = fdopen(fd, "w")) == NULL) {
exitcode = 1;
fprintf(stderr, _("%s: fdopen on %s failed: %s\n"),
progname, fname, strerror(errno));
close(fd);
return NULL;
}
return fp;
}