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