]>
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( | |
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 | ||
79 | static int | |
80 | round_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 | |
112 | char * | |
113 | bbs_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 | |
145 | char * | |
146 | num_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 | ||
172 | char * | |
173 | pct_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 | ||
188 | char * | |
189 | form_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 | ||
204 | char * | |
205 | type_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 | ||
227 | typedef struct { | |
14f8b681 | 228 | uint32_t id; |
0717a7db | 229 | char name[NMAX+1]; |
5aead01d NS |
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( | |
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 | ||
255 | static idcache_t * | |
256 | getnextgrent( | |
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 | ||
270 | static idcache_t * | |
271 | getnextprent( | |
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 | ||
285 | char * | |
286 | uid_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 | ||
322 | char * | |
323 | gid_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 | ||
359 | char * | |
360 | prid_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 | */ | |
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) { | |
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 | } |