]>
Commit | Line | Data |
---|---|---|
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 | ||
19 | char * | |
20 | time_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 | ||
80 | static int | |
81 | round_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 | |
113 | char * | |
114 | bbs_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 | |
146 | char * | |
147 | num_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 | ||
173 | char * | |
174 | pct_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 | ||
189 | char * | |
190 | form_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 | ||
205 | char * | |
206 | type_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 | ||
228 | typedef struct { | |
14f8b681 | 229 | uint32_t id; |
0717a7db | 230 | char name[NMAX+1]; |
5aead01d NS |
231 | } idcache_t; |
232 | ||
233 | static idcache_t uidnc[NID]; | |
234 | static idcache_t gidnc[NID]; | |
235 | static idcache_t pidnc[NID]; | |
236 | static int uentriesleft = NID; | |
237 | static int gentriesleft = NID; | |
238 | static int pentriesleft = NID; | |
239 | ||
240 | static idcache_t * | |
241 | getnextpwent( | |
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 | ||
256 | static idcache_t * | |
257 | getnextgrent( | |
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 | ||
271 | static idcache_t * | |
272 | getnextprent( | |
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 | ||
286 | char * | |
287 | uid_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 | ||
323 | char * | |
324 | gid_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 | ||
360 | char * | |
361 | prid_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 | */ | |
405 | FILE * | |
406 | fopen_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 | } |