]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - quota/util.c
xfs: fix maxicount division by zero error
[thirdparty/xfsprogs-dev.git] / quota / util.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2005 Silicon Graphics, Inc.
4 * All Rights Reserved.
5 */
6
7 #include <sys/types.h>
8 #include <stdbool.h>
9 #include <pwd.h>
10 #include <grp.h>
11 #include <utmp.h>
12 #include "init.h"
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
19 char *
20 time_to_string(
21 time_t origin,
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);
32 timer = max(origin - now, 0);
33 }
34
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
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
79 static int
80 round_snprintf(
81 char *sp,
82 size_t size,
83 const char *fmt_round,
84 const char *fmt_not_round,
85 uint64_t value,
86 uint64_t divisor)
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 */
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)
111
112 char *
113 bbs_to_string(
114 uint64_t v,
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
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)
144
145 char *
146 num_to_string(
147 uint64_t v,
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
172 char *
173 pct_to_string(
174 uint64_t portion,
175 uint64_t whole,
176 char *buf,
177 uint size)
178 {
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;
186 }
187
188 char *
189 form_to_string(
190 uint form)
191 {
192 char *forms[] = {
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
204 char *
205 type_to_string(
206 uint type)
207 {
208 char *types[] = { _("User"), _("Group"), _("Project") };
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
227 typedef struct {
228 uint32_t id;
229 char name[NMAX+1];
230 } idcache_t;
231
232 static idcache_t uidnc[NID];
233 static idcache_t gidnc[NID];
234 static idcache_t pidnc[NID];
235 static int uentriesleft = NID;
236 static int gentriesleft = NID;
237 static int pentriesleft = NID;
238
239 static idcache_t *
240 getnextpwent(
241 uint32_t id,
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;
251 strncpy(idc.name, pw->pw_name, NMAX);
252 return &idc;
253 }
254
255 static idcache_t *
256 getnextgrent(
257 uint32_t id,
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;
266 strncpy(idc.name, gr->gr_name, NMAX);
267 return &idc;
268 }
269
270 static idcache_t *
271 getnextprent(
272 uint32_t id,
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;
281 strncpy(idc.name, pr->pr_name, NMAX);
282 return &idc;
283 }
284
285 char *
286 uid_to_name(
287 uint32_t id)
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
322 char *
323 gid_to_name(
324 uint32_t id)
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
359 char *
360 prid_to_name(
361 uint32_t id)
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 */
404 FILE *
405 fopen_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) {
415 exitcode = 1;
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) {
421 exitcode = 1;
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 }