4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
39 #pragma ident "%Z%%M% %I% %E% SMI"
50 #include <sys/param.h>
51 #include <sys/types.h>
53 #include <sys/filio.h>
54 #include <sys/mntent.h>
56 #include <sys/fs/ufs_quota.h>
58 #include <sys/mnttab.h>
59 #include <sys/vfstab.h>
62 #define LOGINNAMESIZE 8
64 struct username
*u_next
;
66 char u_name
[LOGINNAMESIZE
+ 1];
69 static struct username
*uhead
[UHASH
];
71 static struct username
*lookup(uid_t
);
72 static struct username
*adduid(uid_t
);
73 static int repquota(char *, char *, char *);
74 static void prquota(uid_t
, struct dqblk
*);
75 static void header(void);
76 static void usage(void);
77 static void fmttime(char *, long);
78 static char *hasvfsopt(struct vfstab
*, char *);
79 static int quotactl(int, char *, uid_t
, caddr_t
);
80 static int oneof(char *, char **, int);
82 extern char *mntopt();
83 extern char *hasmntopt();
85 static int vflag
; /* verbose */
86 static int aflag
; /* all file systems */
87 static char **listbuf
;
89 #define QFNAME "quotas"
93 #define dbtok(x) ((x) / (1024 / DEV_BSIZE))
95 #define dbtok(x) ((x) * (DEV_BSIZE / 1024))
99 main(int argc
, char **argv
)
102 struct vfstab vfsbuf
;
106 char quotafile
[MAXPATHLEN
];
111 if ((listbuf
= malloc(sizeof (char *) * CHUNK
)) == NULL
) {
112 (void) fprintf(stderr
, "Can't alloc lisbuf array.");
116 while ((opt
= getopt(argc
, argv
, "avV")) != EOF
)
127 /* Print command line */
131 (void) printf("repquota -F ufs ");
132 for (optc
= 1; optc
< argc
; optc
++) {
135 (void) printf(" %s ", optt
);
137 (void) putchar('\n');
146 if (argc
<= optind
&& !aflag
)
150 * Sync quota information to disk (as userdata). On logging
151 * file systems, this operation does nothing because quota
152 * information is treated as metadata. Logging file systems
153 * are dealt with below in repquota().
155 if (quotactl(Q_ALLSYNC
, NULL
, 0, NULL
) < 0 && errno
== EINVAL
&& vflag
)
156 (void) printf("Warning: "
157 "Quotas are not available in this kernel\n");
160 * If aflag go through vfstab and make a list of appropriate
166 if ((vfstab
= fopen(VFSTAB
, "r")) == NULL
) {
167 (void) fprintf(stderr
, "Can't open ");
171 while (getvfsent(vfstab
, &vfsbuf
) == 0) {
173 if (strcmp(vfsbuf
.vfs_fstype
, MNTTYPE_UFS
) != 0 ||
174 (vfsbuf
.vfs_mntopts
== 0) ||
175 hasvfsopt(&vfsbuf
, MNTOPT_RO
) ||
176 (!hasvfsopt(&vfsbuf
, MNTOPT_RQ
) &&
177 !hasvfsopt(&vfsbuf
, MNTOPT_QUOTA
)))
180 *listp
= malloc(strlen(vfsbuf
.vfs_special
) + 1);
181 (void) strcpy(*listp
, vfsbuf
.vfs_special
);
184 /* grow listbuf if needed */
185 if (listcnt
>= listmax
) {
187 listbuf
= realloc(listbuf
,
188 sizeof (char *) * listmax
);
189 if (listbuf
== NULL
) {
190 (void) fprintf(stderr
,
191 "Can't grow listbuf.\n");
194 listp
= &listbuf
[listcnt
];
197 (void) fclose(vfstab
);
201 listp
= &argv
[optind
];
202 listcnt
= argc
- optind
;
204 if ((mtab
= fopen(MNTTAB
, "r")) == NULL
) {
205 (void) fprintf(stderr
, "Can't open ");
209 while (getmntent(mtab
, &mntp
) == 0) {
210 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) == 0 &&
211 !hasmntopt(&mntp
, MNTOPT_RO
) &&
212 (oneof(mntp
.mnt_special
, listp
, listcnt
) ||
213 oneof(mntp
.mnt_mountp
, listp
, listcnt
))) {
214 (void) snprintf(quotafile
, sizeof (quotafile
), "%s/%s",
215 mntp
.mnt_mountp
, QFNAME
);
216 errs
+= repquota(mntp
.mnt_special
,
217 mntp
.mnt_mountp
, quotafile
);
223 (void) fprintf(stderr
, "Cannot report on %s\n", *listp
);
232 repquota(char *fsdev
, char *fsfile
, char *qffile
)
240 (void) printf("%s (%s):\n", fsdev
, fsfile
);
241 qf
= fopen64(qffile
, "r");
246 if (fstat64(fileno(qf
), &statb
) < 0) {
252 * Flush the file system. On logging file systems, this makes
253 * sure that the quota information (as metadata) gets rolled
256 if (ioctl(fileno(qf
), _FIOFFS
, NULL
) == -1) {
258 (void) fprintf(stderr
, "%s: cannot flush file system.\n",
264 for (uid
= 0; uid
<= MAXUID
&& uid
>= 0; uid
++) {
265 (void) fread(&dqbuf
, sizeof (struct dqblk
), 1, qf
);
269 dqbuf
.dqb_curfiles
== 0 && dqbuf
.dqb_curblocks
== 0)
271 prquota(uid
, &dqbuf
);
280 (void) printf(" Block limits"
282 (void) printf("User used soft hard timeleft"
283 " used soft hard timeleft\n");
287 prquota(uid_t uid
, struct dqblk
*dqp
)
291 char ftimeleft
[80], btimeleft
[80];
293 if (dqp
->dqb_bsoftlimit
== 0 && dqp
->dqb_bhardlimit
== 0 &&
294 dqp
->dqb_fsoftlimit
== 0 && dqp
->dqb_fhardlimit
== 0)
296 (void) time(&(tv
.tv_sec
));
300 (void) printf("%-10s", up
->u_name
);
302 (void) printf("#%-9ld", uid
);
303 if (dqp
->dqb_bsoftlimit
&&
304 dqp
->dqb_curblocks
>= dqp
->dqb_bsoftlimit
) {
305 if (dqp
->dqb_btimelimit
== 0)
306 (void) strcpy(btimeleft
, "NOT STARTED");
307 else if (dqp
->dqb_btimelimit
> tv
.tv_sec
)
309 (long)(dqp
->dqb_btimelimit
- tv
.tv_sec
));
311 (void) strcpy(btimeleft
, "EXPIRED");
315 if (dqp
->dqb_fsoftlimit
&& dqp
->dqb_curfiles
>= dqp
->dqb_fsoftlimit
) {
316 if (dqp
->dqb_ftimelimit
== 0)
317 (void) strcpy(ftimeleft
, "NOT STARTED");
318 else if (dqp
->dqb_ftimelimit
> tv
.tv_sec
)
320 (long)(dqp
->dqb_ftimelimit
- tv
.tv_sec
));
322 (void) strcpy(ftimeleft
, "EXPIRED");
326 (void) printf("%c%c %6lu %6lu %6lu %11s %7lu %6lu %6lu %11s\n",
327 (dqp
->dqb_bsoftlimit
&&
328 dqp
->dqb_curblocks
>= dqp
->dqb_bsoftlimit
) ? '+' : '-',
329 (dqp
->dqb_fsoftlimit
&&
330 dqp
->dqb_curfiles
>= dqp
->dqb_fsoftlimit
) ? '+' : '-',
331 dbtok(dqp
->dqb_curblocks
),
332 dbtok(dqp
->dqb_bsoftlimit
),
333 dbtok(dqp
->dqb_bhardlimit
),
342 fmttime(char *buf
, long time
)
346 int c_secs
; /* conversion units in secs */
347 char *c_str
; /* unit string */
349 {60*60*24*28, "months"},
350 {60*60*24*7, "weeks"},
358 (void) strcpy(buf
, "EXPIRED");
361 for (i
= 0; i
< sizeof (cunits
) / sizeof (cunits
[0]); i
++) {
362 if (time
>= cunits
[i
].c_secs
)
365 (void) sprintf(buf
, "%.1f %s",
366 (double)time
/ cunits
[i
].c_secs
, cunits
[i
].c_str
);
370 oneof(char *target
, char **olistp
, int on
)
372 char **listp
= olistp
;
376 if (*listp
&& strcmp(target
, *listp
) == 0) {
385 static struct username
*
391 for (up
= uhead
[uid
% UHASH
]; up
!= 0; up
= up
->u_next
)
392 if (up
->u_uid
== uid
)
394 if ((pwp
= getpwuid((uid_t
)uid
)) == NULL
)
395 return ((struct username
*)0);
396 up
= adduid(pwp
->pw_uid
);
397 (void) strncpy(up
->u_name
, pwp
->pw_name
, sizeof (up
->u_name
));
402 * adduid() should *ONLY* be called from lookup in order
403 * to avoid duplicate entries.
405 static struct username
*
408 struct username
*up
, **uhp
;
410 up
= calloc(1, sizeof (struct username
));
412 (void) fprintf(stderr
,
413 "out of memory for username structures\n");
416 uhp
= &uhead
[uid
% UHASH
];
426 (void) fprintf(stderr
, "ufs usage:\n");
427 (void) fprintf(stderr
, "\trepquota [-v] -a \n");
428 (void) fprintf(stderr
, "\trepquota [-v] filesys ...\n");
433 quotactl(int cmd
, char *special
, uid_t uid
, caddr_t addr
)
437 struct quotctl quota
;
438 char qfile
[MAXPATHLEN
];
443 if ((special
== NULL
) && (cmd
== Q_ALLSYNC
)) {
445 * Find the mount point of the special device. This is
446 * because the ioctl that implements the quotactl call has
447 * to go to a real file, and not to the block device.
449 if ((fstab
= fopen(MNTTAB
, "r")) == NULL
) {
450 (void) fprintf(stderr
, "%s: ", MNTTAB
);
455 while ((status
= getmntent(fstab
, &mntp
)) == NULL
) {
457 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0 ||
458 hasmntopt(&mntp
, MNTOPT_RO
))
461 if ((strlcpy(qfile
, mntp
.mnt_mountp
,
462 sizeof (qfile
)) >= sizeof (qfile
)) ||
463 (strlcat(qfile
, "/" QFNAME
, sizeof (qfile
)) >=
468 /* If we find *ANY* valid "quotas" file, use it */
469 if ((fd
= open64(qfile
, O_RDONLY
)) >= 0)
472 (void) fclose(fstab
);
475 (void) printf("quotactl: no quotas file "
476 "on any mounted file system\n");
483 status
= ioctl(fd
, Q_QUOTACTL
, "a
);
489 hasvfsopt(struct vfstab
*vfs
, char *opt
)
492 static char *tmpopts
;
495 tmpopts
= calloc(256, sizeof (char));
499 (void) strcpy(tmpopts
, vfs
->vfs_mntopts
);
502 for (; *f
; f
= mntopt(&opts
)) {
503 if (strncmp(opt
, f
, strlen(opt
)) == 0)
504 return (f
- tmpopts
+ vfs
->vfs_mntopts
);