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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
35 #include <sys/netconfig.h>
36 #include <sys/mntent.h>
37 #include <sys/mnttab.h>
38 #include <sys/param.h>
41 #include <netconfig.h>
45 #include <sys/fs/ufs_quota.h>
48 #include <rpcsvc/rquota.h>
54 #define QFNAME "quotas" /* name of quota file */
55 #define RPCSVC_CLOSEDOWN 120 /* 2 minutes */
59 struct fsquot
*fsq_next
;
65 struct fsquot
*fsqlist
= NULL
;
67 typedef struct authunix_parms
*authp
;
69 static int request_pending
; /* Request in progress ? */
73 struct fsquot
*findfsq();
78 void log_cant_reply();
82 static libzfs_handle_t
*(*_libzfs_init
)(void);
83 static void (*_libzfs_fini
)(libzfs_handle_t
*);
84 static zfs_handle_t
*(*_zfs_open
)(libzfs_handle_t
*, const char *, int);
85 static void (*_zfs_close
)(zfs_handle_t
*);
86 static int (*_zfs_prop_get_userquota_int
)(zfs_handle_t
*, const char *,
88 static libzfs_handle_t
*g_zfs
= NULL
;
91 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
92 * packages. 'rquotad' supports zfs as an option.
102 if ((hdl
= dlopen("libzfs.so", RTLD_LAZY
)) != NULL
) {
103 _libzfs_init
= (libzfs_handle_t
*(*)(void))dlsym(hdl
,
105 _libzfs_fini
= (void (*)())dlsym(hdl
, "libzfs_fini");
106 _zfs_open
= (zfs_handle_t
*(*)())dlsym(hdl
, "zfs_open");
107 _zfs_close
= (void (*)())dlsym(hdl
, "zfs_close");
108 _zfs_prop_get_userquota_int
= (int (*)())
109 dlsym(hdl
, "zfs_prop_get_userquota_int");
111 if (_libzfs_init
&& _libzfs_fini
&& _zfs_open
&&
112 _zfs_close
&& _zfs_prop_get_userquota_int
)
113 g_zfs
= _libzfs_init();
119 main(int argc
, char *argv
[])
121 register SVCXPRT
*transp
;
126 * If stdin looks like a TLI endpoint, we assume
127 * that we were started by a port monitor. If
128 * t_getstate fails with TBADF, this is not a
131 if (t_getstate(0) != -1 || t_errno
!= TBADF
) {
133 struct netconfig
*nconf
= NULL
;
135 openlog("rquotad", LOG_PID
, LOG_DAEMON
);
137 if ((netid
= getenv("NLSPROVIDER")) == NULL
) {
140 if (t_sync(0) == -1) {
141 syslog(LOG_ERR
, "could not do t_sync");
144 if (t_getinfo(0, &tinfo
) == -1) {
145 syslog(LOG_ERR
, "t_getinfo failed");
148 if (tinfo
.servtype
== T_CLTS
) {
149 if (tinfo
.addr
== INET_ADDRSTRLEN
)
154 syslog(LOG_ERR
, "wrong transport");
158 if ((nconf
= getnetconfigent(netid
)) == NULL
) {
159 syslog(LOG_ERR
, "cannot get transport info");
162 if ((transp
= svc_tli_create(0, nconf
, NULL
, 0, 0)) == NULL
) {
163 syslog(LOG_ERR
, "cannot create server handle");
167 freenetconfigent(nconf
);
169 if (!svc_reg(transp
, RQUOTAPROG
, RQUOTAVERS
, dispatch
, 0)) {
171 "unable to register (RQUOTAPROG, RQUOTAVERS).");
175 (void) sigset(SIGALRM
, (void(*)(int)) closedown
);
176 (void) alarm(RPCSVC_CLOSEDOWN
);
184 * Started from a shell - fork the daemon.
191 perror("rquotad: can't fork");
193 default: /* parent */
198 * Close existing file descriptors, open "/dev/null" as
199 * standard input, output, and error, and detach from
200 * controlling terminal.
203 (void) open("/dev/null", O_RDONLY
);
204 (void) open("/dev/null", O_WRONLY
);
208 openlog("rquotad", LOG_PID
, LOG_DAEMON
);
211 * Create datagram service
213 if (svc_create(dispatch
, RQUOTAPROG
, RQUOTAVERS
, "datagram_v") == 0) {
214 syslog(LOG_ERR
, "couldn't register datagram_v service");
222 syslog(LOG_ERR
, "Error: svc_run shouldn't have returned");
227 dispatch(rqstp
, transp
)
228 register struct svc_req
*rqstp
;
229 register SVCXPRT
*transp
;
234 switch (rqstp
->rq_proc
) {
237 if (!svc_sendreply(transp
, xdr_void
, 0))
238 log_cant_reply(transp
);
241 case RQUOTAPROC_GETQUOTA
:
242 case RQUOTAPROC_GETACTIVEQUOTA
:
243 getquota(rqstp
, transp
);
247 svcerr_noproc(transp
);
257 if (!request_pending
) {
261 if (!t_getinfo(0, &tinfo
) && (tinfo
.servtype
== T_CLTS
))
264 for (i
= 0, openfd
= 0; i
< svc_max_pollfd
&& openfd
< 2; i
++) {
265 if (svc_pollfd
[i
].fd
>= 0)
272 (void) alarm(RPCSVC_CLOSEDOWN
);
276 getzfsquota(uid_t user
, char *dataset
, struct dqblk
*zq
)
278 zfs_handle_t
*zhp
= NULL
;
279 char propname
[ZFS_MAXPROPLEN
];
280 uint64_t userquota
, userused
;
285 if ((zhp
= _zfs_open(g_zfs
, dataset
, ZFS_TYPE_DATASET
)) == NULL
) {
286 syslog(LOG_ERR
, "can not open zfs dataset %s", dataset
);
290 (void) snprintf(propname
, sizeof (propname
), "userquota@%u", user
);
291 if (_zfs_prop_get_userquota_int(zhp
, propname
, &userquota
) != 0) {
296 (void) snprintf(propname
, sizeof (propname
), "userused@%u", user
);
297 if (_zfs_prop_get_userquota_int(zhp
, propname
, &userused
) != 0) {
302 zq
->dqb_bhardlimit
= userquota
/ DEV_BSIZE
;
303 zq
->dqb_bsoftlimit
= userquota
/ DEV_BSIZE
;
304 zq
->dqb_curblocks
= userused
/ DEV_BSIZE
;
310 getquota(rqstp
, transp
)
311 register struct svc_req
*rqstp
;
312 register SVCXPRT
*transp
;
314 struct getquota_args gqa
;
315 struct getquota_rslt gqr
;
321 gqa
.gqa_pathp
= NULL
; /* let xdr allocate the storage */
322 if (!svc_getargs(transp
, xdr_getquota_args
, (caddr_t
)&gqa
)) {
323 svcerr_decode(transp
);
327 * This authentication is really bogus with the current rpc
328 * authentication scheme. One day we will have something for real.
330 if (rqstp
->rq_cred
.oa_flavor
!= AUTH_UNIX
||
331 (((authp
) rqstp
->rq_clntcred
)->aup_uid
!= 0 &&
332 ((authp
) rqstp
->rq_clntcred
)->aup_uid
!= (uid_t
)gqa
.gqa_uid
)) {
333 gqr
.status
= Q_EPERM
;
336 fsqp
= findfsq(gqa
.gqa_pathp
);
338 gqr
.status
= Q_NOQUOTA
;
342 bzero(&dqblk
, sizeof (dqblk
));
343 if (strcmp(fsqp
->fsq_fstype
, MNTTYPE_ZFS
) == 0) {
344 if (getzfsquota(gqa
.gqa_uid
, fsqp
->fsq_devname
, &dqblk
)) {
345 gqr
.status
= Q_NOQUOTA
;
350 if (quotactl(Q_GETQUOTA
, fsqp
->fsq_dir
,
351 (uid_t
)gqa
.gqa_uid
, &dqblk
) != 0) {
353 if ((errno
== ENOENT
) ||
354 (rqstp
->rq_proc
!= RQUOTAPROC_GETQUOTA
)) {
355 gqr
.status
= Q_NOQUOTA
;
360 * If there is no quotas file, don't bother to sync it.
362 if (errno
!= ENOENT
) {
363 if (quotactl(Q_ALLSYNC
, fsqp
->fsq_dir
,
364 (uid_t
)gqa
.gqa_uid
, &dqblk
) < 0 &&
367 "Quotas are not compiled "
369 if (getdiskquota(fsqp
, (uid_t
)gqa
.gqa_uid
,
371 gqr
.status
= Q_NOQUOTA
;
379 * We send the remaining time instead of the absolute time
380 * because clock skew between machines should be much greater
383 #define gqrslt getquota_rslt_u.gqr_rquota
385 gettimeofday(&tv
, NULL
);
386 gqr
.gqrslt
.rq_btimeleft
= dqblk
.dqb_btimelimit
- tv
.tv_sec
;
387 gqr
.gqrslt
.rq_ftimeleft
= dqblk
.dqb_ftimelimit
- tv
.tv_sec
;
391 gqr
.gqrslt
.rq_active
= qactive
;
392 gqr
.gqrslt
.rq_bsize
= DEV_BSIZE
;
393 gqr
.gqrslt
.rq_bhardlimit
= dqblk
.dqb_bhardlimit
;
394 gqr
.gqrslt
.rq_bsoftlimit
= dqblk
.dqb_bsoftlimit
;
395 gqr
.gqrslt
.rq_curblocks
= dqblk
.dqb_curblocks
;
396 gqr
.gqrslt
.rq_fhardlimit
= dqblk
.dqb_fhardlimit
;
397 gqr
.gqrslt
.rq_fsoftlimit
= dqblk
.dqb_fsoftlimit
;
398 gqr
.gqrslt
.rq_curfiles
= dqblk
.dqb_curfiles
;
401 if (!svc_sendreply(transp
, xdr_getquota_rslt
, (caddr_t
)&gqr
))
402 log_cant_reply(transp
);
406 quotactl(cmd
, mountp
, uid
, dqp
)
414 struct quotctl quota
;
415 char mountpoint
[256];
419 if ((mountp
== NULL
) && (cmd
== Q_ALLSYNC
)) {
421 * Find the mount point of any ufs file system. this is
422 * because the ioctl that implements the quotactl call has
423 * to go to a real file, and not to the block device.
425 if ((fstab
= fopen(MNTTAB
, "r")) == NULL
) {
426 syslog(LOG_ERR
, "can not open %s: %m ", MNTTAB
);
430 while ((status
= getmntent(fstab
, &mntp
)) == NULL
) {
431 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0 ||
432 !(hasmntopt(&mntp
, MNTOPT_RQ
) ||
433 hasmntopt(&mntp
, MNTOPT_QUOTA
)))
435 (void) strlcpy(mountpoint
, mntp
.mnt_mountp
,
436 sizeof (mountpoint
));
437 strcat(mountpoint
, "/quotas");
438 if ((fd
= open64(mountpoint
, O_RDWR
)) >= 0)
447 if (mountp
== NULL
|| mountp
[0] == '\0') {
451 (void) strlcpy(mountpoint
, mountp
, sizeof (mountpoint
));
452 strcat(mountpoint
, "/quotas");
454 if ((fd
= open64(mountpoint
, O_RDONLY
)) < 0) {
456 syslog(LOG_ERR
, "can not open %s: %m ", mountpoint
);
462 quota
.addr
= (caddr_t
)dqp
;
464 status
= ioctl(fd
, Q_QUOTACTL
, "a
);
471 * Return the quota information for the given path. Returns NULL if none
480 static time_t lastmtime
= 0; /* mount table's previous mtime */
483 * If we've never looked at the mount table, or it has changed
484 * since the last time, rebuild the list of quota'd file systems
485 * and remember the current mod time for the mount table.
488 if (stat(MNTTAB
, &sb
) < 0) {
489 syslog(LOG_ERR
, "can't stat %s: %m", MNTTAB
);
492 if (lastmtime
== 0 || sb
.st_mtime
!= lastmtime
) {
495 lastmtime
= sb
.st_mtime
;
499 * Try to find the given path in the list of file systems with
505 if (stat(dir
, &sb
) < 0)
508 for (fsqp
= fsqlist
; fsqp
!= NULL
; fsqp
= fsqp
->fsq_next
) {
509 if (sb
.st_dev
== fsqp
->fsq_dev
)
517 setup_zfs(struct mnttab
*mp
)
522 if (stat(mp
->mnt_mountp
, &sb
) < 0)
525 fsqp
= malloc(sizeof (struct fsquot
));
527 syslog(LOG_ERR
, "out of memory");
530 fsqp
->fsq_dir
= strdup(mp
->mnt_mountp
);
531 fsqp
->fsq_devname
= strdup(mp
->mnt_special
);
532 if (fsqp
->fsq_dir
== NULL
|| fsqp
->fsq_devname
== NULL
) {
533 syslog(LOG_ERR
, "out of memory");
537 fsqp
->fsq_fstype
= MNTTYPE_ZFS
;
538 fsqp
->fsq_dev
= sb
.st_dev
;
539 fsqp
->fsq_next
= fsqlist
;
550 char qfilename
[MAXPATHLEN
];
552 mt
= fopen(MNTTAB
, "r");
554 syslog(LOG_ERR
, "can't read %s: %m", MNTTAB
);
558 while (getmntent(mt
, &m
) == 0) {
559 if (strcmp(m
.mnt_fstype
, MNTTYPE_ZFS
) == 0) {
564 if (strcmp(m
.mnt_fstype
, MNTTYPE_UFS
) != 0)
566 if (!hasquota(m
.mnt_mntopts
)) {
567 snprintf(qfilename
, sizeof (qfilename
), "%s/%s",
568 m
.mnt_mountp
, QFNAME
);
569 if (access(qfilename
, F_OK
) < 0)
572 if (stat(m
.mnt_special
, &sb
) < 0 ||
573 (sb
.st_mode
& S_IFMT
) != S_IFBLK
)
575 fsqp
= malloc(sizeof (struct fsquot
));
577 syslog(LOG_ERR
, "out of memory");
580 fsqp
->fsq_dir
= strdup(m
.mnt_mountp
);
581 fsqp
->fsq_devname
= strdup(m
.mnt_special
);
582 if (fsqp
->fsq_dir
== NULL
|| fsqp
->fsq_devname
== NULL
) {
583 syslog(LOG_ERR
, "out of memory");
586 fsqp
->fsq_fstype
= MNTTYPE_UFS
;
587 fsqp
->fsq_dev
= sb
.st_rdev
;
588 fsqp
->fsq_next
= fsqlist
;
595 * Free the memory used by the current list of quota'd file systems. Nulls
602 register struct fsquot
*fsqp
;
604 while ((fsqp
= fsqlist
) != NULL
) {
605 fsqlist
= fsqp
->fsq_next
;
607 free(fsqp
->fsq_devname
);
613 getdiskquota(fsqp
, uid
, dqp
)
619 char qfilename
[MAXPATHLEN
];
621 snprintf(qfilename
, sizeof (qfilename
), "%s/%s", fsqp
->fsq_dir
, QFNAME
);
622 if ((fd
= open64(qfilename
, O_RDONLY
)) < 0)
624 (void) llseek(fd
, (offset_t
)dqoff(uid
), L_SET
);
625 if (read(fd
, dqp
, sizeof (struct dqblk
)) != sizeof (struct dqblk
)) {
630 if (dqp
->dqb_bhardlimit
== 0 && dqp
->dqb_bsoftlimit
== 0 &&
631 dqp
->dqb_fhardlimit
== 0 && dqp
->dqb_fsoftlimit
== 0) {
638 * Get the client's hostname from the transport handle
639 * If the name is not available then return "(anon)".
641 struct nd_hostservlist
*
642 getclientsnames(transp
)
646 struct netconfig
*nconf
;
647 static struct nd_hostservlist
*serv
;
648 static struct nd_hostservlist anon_hsl
;
649 static struct nd_hostserv anon_hs
;
650 static char anon_hname
[] = "(anon)";
651 static char anon_sname
[] = "";
653 /* Set up anonymous client */
654 anon_hs
.h_host
= anon_hname
;
655 anon_hs
.h_serv
= anon_sname
;
657 anon_hsl
.h_hostservs
= &anon_hs
;
660 netdir_free((char *)serv
, ND_HOSTSERVLIST
);
663 nconf
= getnetconfigent(transp
->xp_netid
);
665 syslog(LOG_ERR
, "%s: getnetconfigent failed",
670 nbuf
= svc_getrpccaller(transp
);
672 freenetconfigent(nconf
);
675 if (netdir_getbyaddr(nconf
, &serv
, nbuf
)) {
676 freenetconfigent(nconf
);
679 freenetconfigent(nconf
);
684 log_cant_reply(transp
)
688 struct nd_hostservlist
*clnames
;
691 saverrno
= errno
; /* save error code */
692 clnames
= getclientsnames(transp
);
695 name
= clnames
->h_hostservs
->h_host
;
699 syslog(LOG_ERR
, "couldn't send reply to %s", name
);
701 syslog(LOG_ERR
, "couldn't send reply to %s: %m", name
);
704 char *mntopts
[] = { MNTOPT_QUOTA
, NULL
};
708 * Return 1 if "quota" appears in the options string
718 while (*opts
!= '\0') {
719 if (getsubopt(&opts
, mntopts
, &value
) == QUOTA
)