]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/util.c
xfs_quota: convert time_to_string to use time64_t
[thirdparty/xfsprogs-dev.git] / quota / util.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0
5aead01d 2/*
da23017d
NS
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5aead01d
NS
5 */
6
cfe6e3f0 7#include <sys/types.h>
29647c8d 8#include <stdbool.h>
5aead01d
NS
9#include <pwd.h>
10#include <grp.h>
11#include <utmp.h>
2a1888c5 12#include "init.h"
5aead01d
NS
13#include "quota.h"
14
15#define SECONDS_IN_A_DAY (24 * 60 * 60)
16#define SECONDS_IN_A_HOUR (60 * 60)
17#define SECONDS_IN_A_MINUTE (60)
18
19char *
20time_to_string(
219285ad 21 time64_t origin,
5aead01d
NS
22 uint flags)
23{
24 static char timestamp[32];
219285ad
DW
25 time64_t timer;
26 time_t now;
5aead01d
NS
27 uint days, hours, minutes, seconds;
28
29 if (flags & ABSOLUTE_FLAG) {
30 timer = origin;
31 } else {
32 time(&now);
68d16907 33 timer = max(origin - now, 0);
5aead01d 34 }
0df88e17 35
f61be1b4
ES
36 /*
37 * If we are in verbose mode, or if less than a day remains, we
38 * will show "X days hh:mm:ss" so the user knows the exact timer status.
39 *
40 * Otherwise, we round down to the nearest day - so we add 30s here
41 * such that setting and reporting a limit in rapid succession will
42 * show the limit which was just set, rather than immediately reporting
43 * one day less.
44 */
45 if ((timer > SECONDS_IN_A_DAY) && !(flags & VERBOSE_FLAG))
46 timer += 30; /* seconds */
47
5aead01d
NS
48 days = timer / SECONDS_IN_A_DAY;
49 if (days)
50 timer %= SECONDS_IN_A_DAY;
51 hours = timer / SECONDS_IN_A_HOUR;
52 if (hours)
53 timer %= SECONDS_IN_A_HOUR;
54 minutes = timer / SECONDS_IN_A_MINUTE;
55 seconds = timer % SECONDS_IN_A_MINUTE;
56
57 if (flags & LIMIT_FLAG) {
58 snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ?
59 _("[-none-]") : _("[--none--]"));
60 } else if (origin == 0) {
61 snprintf(timestamp, sizeof(timestamp), (flags & HUMAN_FLAG) ?
62 _("[------]") : _("[--------]"));
63 } else if ((hours == 0 && minutes == 0 && seconds == 0) ||
64 (!(flags & VERBOSE_FLAG) && days > 0)) {
65 snprintf(timestamp, sizeof(timestamp), "[%u %s]",
66 days, days == 1 ? _("day") : _("days"));
67 } else if (flags & VERBOSE_FLAG) {
68 snprintf(timestamp, sizeof(timestamp), "[%u %s %02u:%02u:%02u]",
69 days, days == 1 ? _("day") : _("days"),
70 hours, minutes, seconds);
71 } else { /* non-verbose, less than a day remaining */
72 snprintf(timestamp, sizeof(timestamp),
73 (flags & HUMAN_FLAG) ?
74 "%02u:%02u:%02u" : "[%02u:%02u:%02u]",
75 hours, minutes, seconds);
76 }
77 return timestamp;
78}
79
80static int
81round_snprintf(
82 char *sp,
83 size_t size,
84 const char *fmt_round,
85 const char *fmt_not_round,
14f8b681
DW
86 uint64_t value,
87 uint64_t divisor)
5aead01d
NS
88{
89 double v = (double)value / divisor;
90
91 value /= divisor;
92 if (v == (double)value)
93 return snprintf(sp, size, fmt_round, (uint)value);
94 else
95 return snprintf(sp, size, fmt_not_round, v);
96}
97
98/* Basic blocks (512) bytes are returned from quotactl */
14f8b681
DW
99#define BBS_TO_EXABYTES(bbs) ((uint64_t)(bbs)>>51)
100#define BBS_TO_PETABYTES(bbs) ((uint64_t)(bbs)>>41)
101#define BBS_TO_TERABYTES(bbs) ((uint64_t)(bbs)>>31)
102#define BBS_TO_GIGABYTES(bbs) ((uint64_t)(bbs)>>21)
103#define BBS_TO_MEGABYTES(bbs) ((uint64_t)(bbs)>>11)
104#define BBS_TO_KILOBYTES(bbs) ((uint64_t)(bbs)>>1)
105
106#define BBEXABYTE ((uint64_t)1<<51)
107#define BBPETABYTE ((uint64_t)1<<41)
108#define BBTERABYTE ((uint64_t)1<<31)
109#define BBGIGABYTE ((uint64_t)1<<21)
110#define BBMEGABYTE ((uint64_t)1<<11)
111#define BBKILOBYTE ((uint64_t)1<< 1)
5aead01d
NS
112
113char *
114bbs_to_string(
14f8b681 115 uint64_t v,
5aead01d
NS
116 char *sp,
117 uint size)
118{
119 if (v == 0)
120 snprintf(sp, size, "%4u", (uint)v);
121 else if (BBS_TO_EXABYTES(v))
122 round_snprintf(sp, size, "%3uE", "%3.1fE", v, BBEXABYTE);
123 else if (BBS_TO_PETABYTES(v))
124 round_snprintf(sp, size, "%3uP", "%3.1fP", v, BBPETABYTE);
125 else if (BBS_TO_TERABYTES(v))
126 round_snprintf(sp, size, "%3uT", "%3.1fT", v, BBTERABYTE);
127 else if (BBS_TO_GIGABYTES(v))
128 round_snprintf(sp, size, "%3uG", "%3.1fG", v, BBGIGABYTE);
129 else if (BBS_TO_MEGABYTES(v))
130 round_snprintf(sp, size, "%3uM", "%3.1fM", v, BBMEGABYTE);
131 else if (BBS_TO_KILOBYTES(v))
132 round_snprintf(sp, size, "%3uK", "%3.1fK", v, BBKILOBYTE);
133 else
134 snprintf(sp, size, "%4u", (uint)v << BBSHIFT); /* bytes */
135 return sp;
136}
137
14f8b681
DW
138#define THOUSAND ((uint64_t)1000)
139#define MILLION ((uint64_t)1000*1000)
140#define BILLION ((uint64_t)1000*1000*1000)
141#define TRILLION ((uint64_t)1000*1000*1000*1000)
142#define GAZILLION ((uint64_t)1000*1000*1000*1000*1000)
143#define RIDICULOUS ((uint64_t)1000*1000*1000*1000*1000*1000)
144#define STOPALREADY ((uint64_t)1000*1000*1000*1000*1000*1000*1000)
5aead01d
NS
145
146char *
147num_to_string(
14f8b681 148 uint64_t v,
5aead01d
NS
149 char *sp,
150 uint size)
151{
152 if (v == 0)
153 snprintf(sp, size, "%4u", (uint)v);
154 else if (v > STOPALREADY)
155 round_snprintf(sp, size, "%3us", "%3.1fs", v, STOPALREADY);
156 else if (v > RIDICULOUS)
157 round_snprintf(sp, size, "%3ur", "%3.1fr", v, RIDICULOUS);
158 else if (v > GAZILLION)
159 round_snprintf(sp, size, "%3ug", "%3.1fg", v, GAZILLION);
160 else if (v > TRILLION)
161 round_snprintf(sp, size, "%3ut", "%3.1ft", v, TRILLION);
162 else if (v > BILLION)
163 round_snprintf(sp, size, "%3ub", "%3.1fb", v, BILLION);
164 else if (v > MILLION)
165 round_snprintf(sp, size, "%3um", "%3.1fm", v, MILLION);
166 else if (v > THOUSAND)
167 round_snprintf(sp, size, "%3uk", "%3.1fk", v, THOUSAND);
168 else
169 snprintf(sp, size, "%4u", (uint)v);
170 return sp;
171}
172
173char *
174pct_to_string(
14f8b681
DW
175 uint64_t portion,
176 uint64_t whole,
481d1f83 177 char *buf,
5aead01d
NS
178 uint size)
179{
481d1f83
AE
180 uint percent;
181
182 percent = whole ? (uint) (100.0 * portion / whole + 0.5) : 0;
183 if (snprintf(buf, size, "%3u", percent) < 0)
184 return "???";
185
186 return buf;
5aead01d
NS
187}
188
189char *
190form_to_string(
191 uint form)
192{
8b3de41d 193 char *forms[] = {
5aead01d
NS
194 _("Blocks"), _("Inodes"), _("Realtime Blocks") };
195
196 if (form & XFS_BLOCK_QUOTA)
197 return forms[0];
198 if (form & XFS_INODE_QUOTA)
199 return forms[1];
200 if (form & XFS_RTBLOCK_QUOTA)
201 return forms[2];
202 return NULL;
203}
204
205char *
206type_to_string(
207 uint type)
208{
8b3de41d 209 char *types[] = { _("User"), _("Group"), _("Project") };
5aead01d
NS
210
211 if (type & XFS_USER_QUOTA)
212 return types[0];
213 if (type & XFS_GROUP_QUOTA)
214 return types[1];
215 if (type & XFS_PROJ_QUOTA)
216 return types[2];
217 return NULL;
218}
219
220
221/*
222 * Identifier caches - user/group/project names/IDs
223 */
224
225#define NID 4096
226#define IDMASK (NID-1)
227
228typedef struct {
14f8b681 229 uint32_t id;
0717a7db 230 char name[NMAX+1];
5aead01d
NS
231} idcache_t;
232
233static idcache_t uidnc[NID];
234static idcache_t gidnc[NID];
235static idcache_t pidnc[NID];
236static int uentriesleft = NID;
237static int gentriesleft = NID;
238static int pentriesleft = NID;
239
240static idcache_t *
241getnextpwent(
14f8b681 242 uint32_t id,
5aead01d
NS
243 int byid)
244{
245 struct passwd *pw;
246 static idcache_t idc;
247
248 /* /etc/passwd */
249 if ((pw = byid? getpwuid(id) : getpwent()) == NULL)
250 return NULL;
251 idc.id = pw->pw_uid;
0717a7db 252 strncpy(idc.name, pw->pw_name, NMAX);
5aead01d
NS
253 return &idc;
254}
255
256static idcache_t *
257getnextgrent(
14f8b681 258 uint32_t id,
5aead01d
NS
259 int byid)
260{
261 struct group *gr;
262 static idcache_t idc;
263
264 if ((gr = byid? getgrgid(id) : getgrent()) == NULL)
265 return NULL;
266 idc.id = gr->gr_gid;
0717a7db 267 strncpy(idc.name, gr->gr_name, NMAX);
5aead01d
NS
268 return &idc;
269}
270
271static idcache_t *
272getnextprent(
14f8b681 273 uint32_t id,
5aead01d
NS
274 int byid)
275{
276 fs_project_t *pr;
277 static idcache_t idc;
278
279 if ((pr = byid? getprprid(id) : getprent()) == NULL)
280 return NULL;
281 idc.id = pr->pr_prid;
0717a7db 282 strncpy(idc.name, pr->pr_name, NMAX);
5aead01d
NS
283 return &idc;
284}
285
286char *
287uid_to_name(
14f8b681 288 uint32_t id)
5aead01d
NS
289{
290 idcache_t *ncp, *idp;
291
292 /* Check cache for name first */
293 ncp = &uidnc[id & IDMASK];
294 if (ncp->id == id && ncp->name[0])
295 return ncp->name;
296 if (uentriesleft) {
297 /*
298 * Fill this cache while seaching for a name.
299 * This lets us run through the file serially.
300 */
301 if (uentriesleft == NID)
302 setpwent();
303 while (((idp = getnextpwent(id, 0)) != NULL) && uentriesleft) {
304 uentriesleft--;
305 ncp = &uidnc[idp->id & IDMASK];
306 if (ncp->name[0] == '\0' || idp->id == id)
307 memcpy(ncp, idp, sizeof(idcache_t));
308 if (idp->id == id)
309 return ncp->name;
310 }
311 endpwent();
312 uentriesleft = 0;
313 ncp = &uidnc[id & IDMASK];
314 }
315
316 /* Not cached - do it the slow way & insert into cache */
317 if ((idp = getnextpwent(id, 1)) == NULL)
318 return NULL;
319 memcpy(ncp, idp, sizeof(idcache_t));
320 return ncp->name;
321}
322
323char *
324gid_to_name(
14f8b681 325 uint32_t id)
5aead01d
NS
326{
327 idcache_t *ncp, *idp;
328
329 /* Check cache for name first */
330 ncp = &gidnc[id & IDMASK];
331 if (ncp->id == id && ncp->name[0])
332 return ncp->name;
333 if (gentriesleft) {
334 /*
335 * Fill this cache while seaching for a name.
336 * This lets us run through the file serially.
337 */
338 if (gentriesleft == NID)
339 setgrent();
340 while (((idp = getnextgrent(id, 0)) != NULL) && gentriesleft) {
341 gentriesleft--;
342 ncp = &gidnc[idp->id & IDMASK];
343 if (ncp->name[0] == '\0' || idp->id == id)
344 memcpy(ncp, idp, sizeof(idcache_t));
345 if (idp->id == id)
346 return ncp->name;
347 }
348 endgrent();
349 gentriesleft = 0;
350 ncp = &gidnc[id & IDMASK];
351 }
352
353 /* Not cached - do it the slow way & insert into cache */
354 if ((idp = getnextgrent(id, 1)) == NULL)
355 return NULL;
356 memcpy(ncp, idp, sizeof(idcache_t));
357 return ncp->name;
358}
359
360char *
361prid_to_name(
14f8b681 362 uint32_t id)
5aead01d
NS
363{
364 idcache_t *ncp, *idp;
365
366 /* Check cache for name first */
367 ncp = &pidnc[id & IDMASK];
368 if (ncp->id == id && ncp->name[0])
369 return ncp->name;
370 if (pentriesleft) {
371 /*
372 * Fill this cache while seaching for a name.
373 * This lets us run through the file serially.
374 */
375 if (pentriesleft == NID)
376 setprent();
377 while (((idp = getnextprent(id, 0)) != NULL) && pentriesleft) {
378 pentriesleft--;
379 ncp = &pidnc[idp->id & IDMASK];
380 if (ncp->name[0] == '\0' || idp->id == id)
381 memcpy(ncp, idp, sizeof(idcache_t));
382 if (idp->id == id)
383 return ncp->name;
384 }
385 endprent();
386 pentriesleft = 0;
387 ncp = &pidnc[id & IDMASK];
388 }
389
390 /* Not cached - do it the slow way & insert into cache */
391 if ((idp = getnextprent(id, 1)) == NULL)
392 return NULL;
393 memcpy(ncp, idp, sizeof(idcache_t));
394 return ncp->name;
395}
396
397
398/*
399 * Utility routine for opening an output file so that it can
400 * be "securely" written to (i.e. without vulnerability to a
401 * symlink attack).
402 *
403 * Returns NULL on failure, stdout on NULL input.
404 */
405FILE *
406fopen_write_secure(
407 char *fname)
408{
409 FILE *fp;
410 int fd;
411
412 if (!fname)
413 return stdout;
414
415 if ((fd = open(fname, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) {
e3210fd8 416 exitcode = 1;
5aead01d
NS
417 fprintf(stderr, _("%s: open on %s failed: %s\n"),
418 progname, fname, strerror(errno));
419 return NULL;
420 }
421 if ((fp = fdopen(fd, "w")) == NULL) {
e3210fd8 422 exitcode = 1;
5aead01d
NS
423 fprintf(stderr, _("%s: fdopen on %s failed: %s\n"),
424 progname, fname, strerror(errno));
425 close(fd);
426 return NULL;
427 }
428 return fp;
429}