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