Initial commit to Gerrit
[profile/ivi/quota.git] / quot.c
1 /*
2  * Copyright (c) 1980, 1990 Regents of the University of California.
3  * Copyright (C) 2000, 2001 Silicon Graphics, Inc. [SGI]
4  * All rights reserved.
5  *
6  * [Extensions to support XFS are copyright SGI]
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met: 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer. 2.
12  * Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution. 3. All advertising
15  * materials mentioning features or use of this software must display the
16  * following acknowledgement: This product includes software developed by the
17  * University of California, Berkeley and its contributors. 4. Neither the
18  * name of the University nor the names of its contributors may be used to
19  * endorse or promote products derived from this software without specific
20  * prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
23  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
26  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include "config.h"
36
37 #include <sys/stat.h>
38 #include <sys/param.h>
39 #include <sys/ioctl.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <time.h>
47 #include <utmp.h>
48 #include <pwd.h>
49 #include <grp.h>
50
51 #include "pot.h"
52 #include "quot.h"
53 #include "common.h"
54 #include "mntopt.h"
55 #include "bylabel.h"
56 #include "quotasys.h"
57
58 #define TSIZE   500
59 __uint64_t sizes[TSIZE];
60 __uint64_t overflow;
61
62 static int aflag;
63 static int cflag;
64 static int fflag;
65 static int gflag;
66 static int uflag;
67 static int vflag;
68 static int iflag;
69 static int qflag;
70 static int Tflag;
71 static time_t now;
72 char *progname;
73
74 static void mounttable(void);
75 static char *idname(__uint32_t, int);
76 static void report(const char *, char *, int);
77 static void creport(const char *, char *);
78
79 static void usage(void)
80 {
81         errstr(_("Usage: %s [-acfugvViTq] [filesystem...]\n"), progname);
82         exit(1);
83 }
84
85 int main(int argc, char **argv)
86 {
87         int c;
88
89         now = time(0);
90         progname = basename(argv[0]);
91
92         while ((c = getopt(argc, argv, "acfguvVTq")) != -1) {
93                 switch (c) {
94                   case 'a':
95                           aflag++;
96                           break;
97                   case 'c':
98                           cflag++;
99                           break;
100                   case 'f':
101                           fflag++;
102                           break;
103                   case 'g':
104                           gflag++;
105                           break;
106                   case 'u':
107                           uflag++;
108                           break;
109                   case 'v':
110                           vflag++;
111                           break;
112                   case 'i':
113                           iflag++;
114                           break;
115                   case 'q':
116                           qflag++;
117                           break;
118                   case 'T':
119                           Tflag++;
120                           break;
121                   case 'V':
122                           version();
123                           exit(0);
124                   default:
125                           usage();
126                 }
127         }
128         if ((aflag && optind != argc) || (!aflag && optind == argc))
129                 usage();
130         if (!uflag && !gflag)
131                 uflag++;
132         if (init_mounts_scan(aflag ? 0 : argc - optind, argv + optind, (iflag ? MS_NO_AUTOFS : 0)) < 0)
133                 return 1;
134         mounttable();
135         end_mounts_scan();
136         return 0;
137 }
138
139 static void mounttable(void)
140 {
141         int doit = 0;
142         struct mntent *mntp;
143
144         while ((mntp = get_next_mount())) {
145                 /* Currently, only XFS is implemented... */
146                 if (strcmp(mntp->mnt_type, MNTTYPE_XFS) == 0) {
147                         checkXFS(mntp->mnt_fsname, mntp->mnt_dir);
148                         doit = 1;
149                 }
150                 /* ...additional filesystems types here. */
151
152                 if (doit) {
153                         if (cflag) creport(mntp->mnt_fsname, mntp->mnt_dir);
154                         if (!cflag && uflag) report(mntp->mnt_fsname, mntp->mnt_dir, 0);
155                         if (!cflag && gflag) report(mntp->mnt_fsname, mntp->mnt_dir, 1);
156                 }
157         }
158 }
159
160 static int qcmp(du_t * p1, du_t * p2)
161 {
162         if (p1->blocks > p2->blocks)
163                 return -1;
164         if (p1->blocks < p2->blocks)
165                 return 1;
166         if (p1->id > p2->id)
167                 return 1;
168         else if (p1->id < p2->id)
169                 return -1;
170         return 0;
171 }
172
173 static void creport(const char *file, char *fsdir)
174 {
175         int i;
176         __uint64_t t = 0;
177
178         printf(_("%s (%s):\n"), file, fsdir);
179         for (i = 0; i < TSIZE - 1; i++)
180                 if (sizes[i] > 0) {
181                         t += sizes[i] * i;
182                         printf(_("%d\t%llu\t%llu\n"), i,
183                                (unsigned long long) sizes[i],
184                                (unsigned long long) t);
185                 }
186         printf(_("%d\t%llu\t%llu\n"), TSIZE - 1,
187                (unsigned long long) sizes[TSIZE - 1],
188                (unsigned long long) (overflow + t));
189 }
190
191 static void report(const char *file, char *fsdir, int type)
192 {
193         du_t *dp;
194
195         printf(_("%s (%s) %s:\n"), file, fsdir, type? "groups" : "users");
196         if (!qflag)
197                 qsort(du[type], ndu[type], sizeof(du[type][0]), (int (*)(const void *, const void *))qcmp);
198         for (dp = du[type]; dp < &du[type][ndu[type]]; dp++) {
199                 char *cp;
200
201                 if (dp->blocks == 0)
202                         return;
203                 printf(_("%8llu    "), (unsigned long long) dp->blocks);
204                 if (fflag)
205                         printf(_("%8llu    "), (unsigned long long) dp->nfiles);
206                 if ((cp = idname(dp->id, type)) != NULL) {
207                         if (Tflag)
208                                 printf(_("%s"), cp);
209                         else
210                                 printf(_("%-8.8s"), cp);
211                 } else
212                         printf(_("#%-7d"), dp->id);
213                 if (vflag)
214                         printf(_("    %8llu    %8llu    %8llu"),
215                                (unsigned long long) dp->blocks30,
216                                (unsigned long long) dp->blocks60,
217                                (unsigned long long) dp->blocks90);
218                 putchar('\n');
219         }
220 }
221
222 static idcache_t *getnextent(int type, __uint32_t id, int byid)
223 {
224         struct passwd *pw;
225         struct group  *gr;
226         static idcache_t idc;
227
228         if (type) {     /* /etc/group */
229                 if ((gr = byid? getgrgid(id) : getgrent()) == NULL)
230                         return NULL;
231                 idc.id = gr->gr_gid;
232                 strncpy(idc.name, gr->gr_name, UT_NAMESIZE);
233                 return &idc;
234         }
235         /* /etc/passwd */
236         if ((pw = byid? getpwuid(id) : getpwent()) == NULL)
237                 return NULL;
238         idc.id = pw->pw_uid;
239         strncpy(idc.name, pw->pw_name, UT_NAMESIZE);
240         return &idc;
241 }
242
243 static char *idname(__uint32_t id, int type)
244 {
245         idcache_t *ncp, *idp;
246         static idcache_t nc[2][NID];
247         static int entriesleft[2] = { NID, NID };
248
249         /* check cache for name first */
250         ncp = &nc[type][id & IDMASK];
251         if (ncp->id == id && ncp->name[0])
252                 return ncp->name;
253         if (entriesleft[type]) {
254                 /*
255                  * If we haven't gone through the passwd/group file
256                  * then fill the cache while seaching for name.
257                  * This lets us run through passwd/group serially.
258                  */
259                 if (entriesleft[type] == NID)
260                         type? setgrent() : setpwent();
261                 while (((idp = getnextent(type, id, 0)) != NULL) && entriesleft[type]) {
262                         entriesleft[type]--;
263                         ncp = &nc[type][idp->id & IDMASK];
264                         if (ncp->name[0] == '\0' || idp->id == id)
265                                 memcpy(ncp, idp, sizeof(idcache_t));
266                         if (idp->id == id)
267                                 return ncp->name;
268                 }
269                 type? endgrent() : endpwent();
270                 entriesleft[type] = 0;
271                 ncp = &nc[type][id & IDMASK];
272         }
273
274         /* Not cached - do it the slow way & insert into cache */
275         if ((idp = getnextent(type, id, 1)) == NULL)
276                 return NULL;
277         memcpy(ncp, idp, sizeof(idcache_t));
278         return ncp->name;
279 }
280
281 /*
282  *      === XFS specific code follows ===
283  */
284
285 static void acctXFS(xfs_bstat_t *p)
286 {
287         register du_t *dp;
288         du_t **hp;
289         __uint64_t size;
290         __uint32_t i, id;
291
292         if ((p->bs_mode & S_IFMT) == 0)
293                 return;
294         size = howmany((p->bs_blocks * p->bs_blksize), 0x400ULL);
295
296         if (cflag) {
297                 if (!(S_ISDIR(p->bs_mode) || S_ISREG(p->bs_mode)))
298                         return;
299                 if (size >= TSIZE) {
300                         overflow += size;
301                         size = TSIZE - 1;
302                 }
303                 sizes[(int)size]++;
304                 return;
305         }
306         for (i = 0; i < 2; i++) {
307                 id = (i == 0)? p->bs_uid : p->bs_gid;
308                 hp = &duhash[i][id % DUHASH];
309                 for (dp = *hp; dp; dp = dp->next)
310                         if (dp->id == id)
311                                 break;
312                 if (dp == 0) {
313                         if (ndu[i] >= NDU)
314                                 return;
315                         dp = &du[i][(ndu[i]++)];
316                         dp->next = *hp;
317                         *hp = dp;
318                         dp->id = id;
319                         dp->nfiles = 0;
320                         dp->blocks = 0;
321                         dp->blocks30 = 0;
322                         dp->blocks60 = 0;
323                         dp->blocks90 = 0;
324                 }
325                 dp->blocks += size;
326
327                 if (now - p->bs_atime.tv_sec > 30 * SEC24HR)
328                         dp->blocks30 += size;
329                 if (now - p->bs_atime.tv_sec > 60 * SEC24HR)
330                         dp->blocks60 += size;
331                 if (now - p->bs_atime.tv_sec > 90 * SEC24HR)
332                         dp->blocks90 += size;
333                 dp->nfiles++;
334         }
335 }
336
337 static void checkXFS(const char *file, char *fsdir)
338 {
339         xfs_fsop_bulkreq_t bulkreq;
340         __u64 last = 0;
341         __s32 count;
342         int i;
343         int sts;
344         int fsfd;
345         du_t **dp;
346         xfs_bstat_t *buf;
347
348         /*
349          * Initialize tables between checks; because of the qsort
350          * in report() the hash tables must be rebuilt each time.
351          */
352         for (sts = 0; sts < TSIZE; sts++)
353                 sizes[sts] = 0;
354         overflow = 0;
355         for (i = 0; i < 2; i++)
356                 for (dp = duhash[i]; dp < &duhash[i][DUHASH]; dp++)
357                         *dp = 0;
358         ndu[0] = ndu[1] = 0;
359
360         fsfd = open(fsdir, O_RDONLY);
361         if (fsfd < 0) {
362                 errstr(_("cannot open %s: %s\n"), fsdir, strerror(errno));
363                 exit(1);
364         }
365         sync();
366
367         buf = (xfs_bstat_t *) smalloc(NBSTAT * sizeof(xfs_bstat_t));
368         memset(buf, 0, NBSTAT * sizeof(xfs_bstat_t));
369
370         bulkreq.lastip = &last;
371         bulkreq.icount = NBSTAT;
372         bulkreq.ubuffer = buf;
373         bulkreq.ocount = &count;
374
375         while ((sts = ioctl(fsfd, XFS_IOC_FSBULKSTAT, &bulkreq)) == 0) {
376                 if (count == 0)
377                         break;
378                 for (i = 0; i < count; i++)
379                         acctXFS(&buf[i]);
380         }
381         if (sts < 0) {
382                 errstr(_("XFS_IOC_FSBULKSTAT ioctl failed: %s\n"),
383                         strerror(errno));
384                 exit(1);
385         }
386         free(buf);
387         close(fsfd);
388 }