]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - quota/util.c
xfs_metadump: properly handle obfuscation of all remote attribute blocks
[thirdparty/xfsprogs-dev.git] / quota / util.c
CommitLineData
5aead01d 1/*
da23017d
NS
2 * Copyright (c) 2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
5aead01d 4 *
da23017d
NS
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
5aead01d
NS
7 * published by the Free Software Foundation.
8 *
da23017d
NS
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.
5aead01d 13 *
da23017d
NS
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
5aead01d
NS
17 */
18
cfe6e3f0 19#include <sys/types.h>
29647c8d 20#include <stdbool.h>
5aead01d
NS
21#include <pwd.h>
22#include <grp.h>
23#include <utmp.h>
2a1888c5 24#include "init.h"
5aead01d
NS
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
31char *
32time_to_string(
44214b5a 33 time_t origin,
5aead01d
NS
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 }
0df88e17 46
f61be1b4
ES
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
5aead01d
NS
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
91static int
92round_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
124char *
125bbs_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
157char *
158num_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
184char *
185pct_to_string(
481d1f83
AE
186 __uint64_t portion,
187 __uint64_t whole,
188 char *buf,
5aead01d
NS
189 uint size)
190{
481d1f83
AE
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;
5aead01d
NS
198}
199
200char *
201form_to_string(
202 uint form)
203{
8b3de41d 204 char *forms[] = {
5aead01d
NS
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
216char *
217type_to_string(
218 uint type)
219{
8b3de41d 220 char *types[] = { _("User"), _("Group"), _("Project") };
5aead01d
NS
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
239typedef struct {
240 __uint32_t id;
0717a7db 241 char name[NMAX+1];
5aead01d
NS
242} idcache_t;
243
244static idcache_t uidnc[NID];
245static idcache_t gidnc[NID];
246static idcache_t pidnc[NID];
247static int uentriesleft = NID;
248static int gentriesleft = NID;
249static int pentriesleft = NID;
250
251static idcache_t *
252getnextpwent(
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;
0717a7db 263 strncpy(idc.name, pw->pw_name, NMAX);
5aead01d
NS
264 return &idc;
265}
266
267static idcache_t *
268getnextgrent(
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;
0717a7db 278 strncpy(idc.name, gr->gr_name, NMAX);
5aead01d
NS
279 return &idc;
280}
281
282static idcache_t *
283getnextprent(
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;
0717a7db 293 strncpy(idc.name, pr->pr_name, NMAX);
5aead01d
NS
294 return &idc;
295}
296
297char *
298uid_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
334char *
335gid_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
371char *
372prid_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 */
416FILE *
417fopen_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) {
e3210fd8 427 exitcode = 1;
5aead01d
NS
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) {
e3210fd8 433 exitcode = 1;
5aead01d
NS
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}