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 2017 Joyent Inc
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
36 #include <sys/netconfig.h>
37 #include <sys/mntent.h>
38 #include <sys/mnttab.h>
39 #include <sys/param.h>
41 #include <sys/debug.h>
43 #include <netconfig.h>
47 #include <sys/fs/ufs_quota.h>
50 #include <rpcsvc/rquota.h>
56 #define QFNAME "quotas" /* name of quota file */
57 #define RPCSVC_CLOSEDOWN 120 /* 2 minutes */
61 struct fsquot
*fsq_next
;
67 struct fsquot
*fsqlist
= NULL
;
69 typedef struct authunix_parms
*authp
;
71 static int request_pending
; /* Request in progress ? */
75 struct fsquot
*findfsq();
80 void log_cant_reply();
84 static libzfs_handle_t
*(*_libzfs_init
)(void);
85 static void (*_libzfs_fini
)(libzfs_handle_t
*);
86 static zfs_handle_t
*(*_zfs_open
)(libzfs_handle_t
*, const char *, int);
87 static void (*_zfs_close
)(zfs_handle_t
*);
88 static int (*_zfs_prop_get_userquota_int
)(zfs_handle_t
*, const char *,
90 static libzfs_handle_t
*g_zfs
= NULL
;
93 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
94 * packages. 'rquotad' supports zfs as an option.
104 if ((hdl
= dlopen("libzfs.so", RTLD_LAZY
)) != NULL
) {
105 _libzfs_init
= (libzfs_handle_t
*(*)(void))dlsym(hdl
,
107 _libzfs_fini
= (void (*)())dlsym(hdl
, "libzfs_fini");
108 _zfs_open
= (zfs_handle_t
*(*)())dlsym(hdl
, "zfs_open");
109 _zfs_close
= (void (*)())dlsym(hdl
, "zfs_close");
110 _zfs_prop_get_userquota_int
= (int (*)())
111 dlsym(hdl
, "zfs_prop_get_userquota_int");
113 if (_libzfs_init
&& _libzfs_fini
&& _zfs_open
&&
114 _zfs_close
&& _zfs_prop_get_userquota_int
)
115 g_zfs
= _libzfs_init();
121 main(int argc
, char *argv
[])
123 register SVCXPRT
*transp
;
128 * If stdin looks like a TLI endpoint, we assume
129 * that we were started by a port monitor. If
130 * t_getstate fails with TBADF, this is not a
133 if (t_getstate(0) != -1 || t_errno
!= TBADF
) {
135 struct netconfig
*nconf
= NULL
;
137 openlog("rquotad", LOG_PID
, LOG_DAEMON
);
139 if ((netid
= getenv("NLSPROVIDER")) == NULL
) {
142 if (t_sync(0) == -1) {
143 syslog(LOG_ERR
, "could not do t_sync");
146 if (t_getinfo(0, &tinfo
) == -1) {
147 syslog(LOG_ERR
, "t_getinfo failed");
150 if (tinfo
.servtype
== T_CLTS
) {
151 if (tinfo
.addr
== INET_ADDRSTRLEN
)
156 syslog(LOG_ERR
, "wrong transport");
160 if ((nconf
= getnetconfigent(netid
)) == NULL
) {
161 syslog(LOG_ERR
, "cannot get transport info");
164 if ((transp
= svc_tli_create(0, nconf
, NULL
, 0, 0)) == NULL
) {
165 syslog(LOG_ERR
, "cannot create server handle");
169 freenetconfigent(nconf
);
171 if (!svc_reg(transp
, RQUOTAPROG
, RQUOTAVERS
, dispatch
, 0)) {
173 "unable to register (RQUOTAPROG, RQUOTAVERS).");
177 (void) sigset(SIGALRM
, (void(*)(int)) closedown
);
178 (void) alarm(RPCSVC_CLOSEDOWN
);
186 * Started from a shell - fork the daemon.
193 perror("rquotad: can't fork");
195 default: /* parent */
200 * Close existing file descriptors, open "/dev/null" as
201 * standard input, output, and error, and detach from
202 * controlling terminal.
205 (void) open("/dev/null", O_RDONLY
);
206 (void) open("/dev/null", O_WRONLY
);
210 openlog("rquotad", LOG_PID
, LOG_DAEMON
);
213 * Create datagram service
215 if (svc_create(dispatch
, RQUOTAPROG
, RQUOTAVERS
, "datagram_v") == 0) {
216 syslog(LOG_ERR
, "couldn't register datagram_v service");
224 syslog(LOG_ERR
, "Error: svc_run shouldn't have returned");
229 dispatch(rqstp
, transp
)
230 register struct svc_req
*rqstp
;
231 register SVCXPRT
*transp
;
236 switch (rqstp
->rq_proc
) {
239 if (!svc_sendreply(transp
, xdr_void
, 0))
240 log_cant_reply(transp
);
243 case RQUOTAPROC_GETQUOTA
:
244 case RQUOTAPROC_GETACTIVEQUOTA
:
245 getquota(rqstp
, transp
);
249 svcerr_noproc(transp
);
259 if (!request_pending
) {
263 if (!t_getinfo(0, &tinfo
) && (tinfo
.servtype
== T_CLTS
))
266 for (i
= 0, openfd
= 0; i
< svc_max_pollfd
&& openfd
< 2; i
++) {
267 if (svc_pollfd
[i
].fd
>= 0)
274 (void) alarm(RPCSVC_CLOSEDOWN
);
278 getzfsquota(uid_t user
, char *dataset
, struct dqblk
*zq
)
280 zfs_handle_t
*zhp
= NULL
;
281 char propname
[ZFS_MAXPROPLEN
];
282 uint64_t userquota
, userused
;
287 if ((zhp
= _zfs_open(g_zfs
, dataset
, ZFS_TYPE_DATASET
)) == NULL
) {
288 syslog(LOG_ERR
, "can not open zfs dataset %s", dataset
);
292 (void) snprintf(propname
, sizeof (propname
), "userquota@%u", user
);
293 if (_zfs_prop_get_userquota_int(zhp
, propname
, &userquota
) != 0) {
298 (void) snprintf(propname
, sizeof (propname
), "userused@%u", user
);
299 if (_zfs_prop_get_userquota_int(zhp
, propname
, &userused
) != 0) {
304 zq
->dqb_bhardlimit
= userquota
/ DEV_BSIZE
;
305 zq
->dqb_bsoftlimit
= userquota
/ DEV_BSIZE
;
306 zq
->dqb_curblocks
= userused
/ DEV_BSIZE
;
312 getquota(rqstp
, transp
)
313 register struct svc_req
*rqstp
;
314 register SVCXPRT
*transp
;
316 struct getquota_args gqa
;
317 struct getquota_rslt gqr
;
323 gqa
.gqa_pathp
= NULL
; /* let xdr allocate the storage */
324 if (!svc_getargs(transp
, xdr_getquota_args
, (caddr_t
)&gqa
)) {
325 svcerr_decode(transp
);
329 * This authentication is really bogus with the current rpc
330 * authentication scheme. One day we will have something for real.
332 CTASSERT(sizeof (authp
) <= RQCRED_SIZE
);
333 if (rqstp
->rq_cred
.oa_flavor
!= AUTH_UNIX
||
334 (((authp
) rqstp
->rq_clntcred
)->aup_uid
!= 0 &&
335 ((authp
) rqstp
->rq_clntcred
)->aup_uid
!= (uid_t
)gqa
.gqa_uid
)) {
336 gqr
.status
= Q_EPERM
;
339 fsqp
= findfsq(gqa
.gqa_pathp
);
341 gqr
.status
= Q_NOQUOTA
;
345 bzero(&dqblk
, sizeof (dqblk
));
346 if (strcmp(fsqp
->fsq_fstype
, MNTTYPE_ZFS
) == 0) {
347 if (getzfsquota(gqa
.gqa_uid
, fsqp
->fsq_devname
, &dqblk
)) {
348 gqr
.status
= Q_NOQUOTA
;
353 if (quotactl(Q_GETQUOTA
, fsqp
->fsq_dir
,
354 (uid_t
)gqa
.gqa_uid
, &dqblk
) != 0) {
356 if ((errno
== ENOENT
) ||
357 (rqstp
->rq_proc
!= RQUOTAPROC_GETQUOTA
)) {
358 gqr
.status
= Q_NOQUOTA
;
363 * If there is no quotas file, don't bother to sync it.
365 if (errno
!= ENOENT
) {
366 if (quotactl(Q_ALLSYNC
, fsqp
->fsq_dir
,
367 (uid_t
)gqa
.gqa_uid
, &dqblk
) < 0 &&
370 "Quotas are not compiled "
372 if (getdiskquota(fsqp
, (uid_t
)gqa
.gqa_uid
,
374 gqr
.status
= Q_NOQUOTA
;
382 * We send the remaining time instead of the absolute time
383 * because clock skew between machines should be much greater
386 #define gqrslt getquota_rslt_u.gqr_rquota
388 gettimeofday(&tv
, NULL
);
389 gqr
.gqrslt
.rq_btimeleft
= dqblk
.dqb_btimelimit
- tv
.tv_sec
;
390 gqr
.gqrslt
.rq_ftimeleft
= dqblk
.dqb_ftimelimit
- tv
.tv_sec
;
394 gqr
.gqrslt
.rq_active
= qactive
;
395 gqr
.gqrslt
.rq_bsize
= DEV_BSIZE
;
396 gqr
.gqrslt
.rq_bhardlimit
= dqblk
.dqb_bhardlimit
;
397 gqr
.gqrslt
.rq_bsoftlimit
= dqblk
.dqb_bsoftlimit
;
398 gqr
.gqrslt
.rq_curblocks
= dqblk
.dqb_curblocks
;
399 gqr
.gqrslt
.rq_fhardlimit
= dqblk
.dqb_fhardlimit
;
400 gqr
.gqrslt
.rq_fsoftlimit
= dqblk
.dqb_fsoftlimit
;
401 gqr
.gqrslt
.rq_curfiles
= dqblk
.dqb_curfiles
;
404 if (!svc_sendreply(transp
, xdr_getquota_rslt
, (caddr_t
)&gqr
))
405 log_cant_reply(transp
);
409 quotactl(cmd
, mountp
, uid
, dqp
)
417 struct quotctl quota
;
418 char mountpoint
[256];
422 if ((mountp
== NULL
) && (cmd
== Q_ALLSYNC
)) {
424 * Find the mount point of any ufs file system. this is
425 * because the ioctl that implements the quotactl call has
426 * to go to a real file, and not to the block device.
428 if ((fstab
= fopen(MNTTAB
, "r")) == NULL
) {
429 syslog(LOG_ERR
, "can not open %s: %m ", MNTTAB
);
433 while ((status
= getmntent(fstab
, &mntp
)) == 0) {
434 if (strcmp(mntp
.mnt_fstype
, MNTTYPE_UFS
) != 0 ||
435 !(hasmntopt(&mntp
, MNTOPT_RQ
) ||
436 hasmntopt(&mntp
, MNTOPT_QUOTA
)))
438 (void) strlcpy(mountpoint
, mntp
.mnt_mountp
,
439 sizeof (mountpoint
));
440 strcat(mountpoint
, "/quotas");
441 if ((fd
= open64(mountpoint
, O_RDWR
)) >= 0)
450 if (mountp
== NULL
|| mountp
[0] == '\0') {
454 (void) strlcpy(mountpoint
, mountp
, sizeof (mountpoint
));
455 strcat(mountpoint
, "/quotas");
457 if ((fd
= open64(mountpoint
, O_RDONLY
)) < 0) {
459 syslog(LOG_ERR
, "can not open %s: %m ", mountpoint
);
465 quota
.addr
= (caddr_t
)dqp
;
467 status
= ioctl(fd
, Q_QUOTACTL
, "a
);
474 * Return the quota information for the given path. Returns NULL if none
483 static time_t lastmtime
= 0; /* mount table's previous mtime */
486 * If we've never looked at the mount table, or it has changed
487 * since the last time, rebuild the list of quota'd file systems
488 * and remember the current mod time for the mount table.
491 if (stat(MNTTAB
, &sb
) < 0) {
492 syslog(LOG_ERR
, "can't stat %s: %m", MNTTAB
);
495 if (lastmtime
== 0 || sb
.st_mtime
!= lastmtime
) {
498 lastmtime
= sb
.st_mtime
;
502 * Try to find the given path in the list of file systems with
508 if (stat(dir
, &sb
) < 0)
511 for (fsqp
= fsqlist
; fsqp
!= NULL
; fsqp
= fsqp
->fsq_next
) {
512 if (sb
.st_dev
== fsqp
->fsq_dev
)
520 setup_zfs(struct mnttab
*mp
)
525 if (stat(mp
->mnt_mountp
, &sb
) < 0)
528 fsqp
= malloc(sizeof (struct fsquot
));
530 syslog(LOG_ERR
, "out of memory");
533 fsqp
->fsq_dir
= strdup(mp
->mnt_mountp
);
534 fsqp
->fsq_devname
= strdup(mp
->mnt_special
);
535 if (fsqp
->fsq_dir
== NULL
|| fsqp
->fsq_devname
== NULL
) {
536 syslog(LOG_ERR
, "out of memory");
540 fsqp
->fsq_fstype
= MNTTYPE_ZFS
;
541 fsqp
->fsq_dev
= sb
.st_dev
;
542 fsqp
->fsq_next
= fsqlist
;
553 char qfilename
[MAXPATHLEN
];
555 mt
= fopen(MNTTAB
, "r");
557 syslog(LOG_ERR
, "can't read %s: %m", MNTTAB
);
561 while (getmntent(mt
, &m
) == 0) {
562 if (strcmp(m
.mnt_fstype
, MNTTYPE_ZFS
) == 0) {
567 if (strcmp(m
.mnt_fstype
, MNTTYPE_UFS
) != 0)
569 if (!hasquota(m
.mnt_mntopts
)) {
570 snprintf(qfilename
, sizeof (qfilename
), "%s/%s",
571 m
.mnt_mountp
, QFNAME
);
572 if (access(qfilename
, F_OK
) < 0)
575 if (stat(m
.mnt_special
, &sb
) < 0 ||
576 (sb
.st_mode
& S_IFMT
) != S_IFBLK
)
578 fsqp
= malloc(sizeof (struct fsquot
));
580 syslog(LOG_ERR
, "out of memory");
583 fsqp
->fsq_dir
= strdup(m
.mnt_mountp
);
584 fsqp
->fsq_devname
= strdup(m
.mnt_special
);
585 if (fsqp
->fsq_dir
== NULL
|| fsqp
->fsq_devname
== NULL
) {
586 syslog(LOG_ERR
, "out of memory");
589 fsqp
->fsq_fstype
= MNTTYPE_UFS
;
590 fsqp
->fsq_dev
= sb
.st_rdev
;
591 fsqp
->fsq_next
= fsqlist
;
598 * Free the memory used by the current list of quota'd file systems. Nulls
605 register struct fsquot
*fsqp
;
607 while ((fsqp
= fsqlist
) != NULL
) {
608 fsqlist
= fsqp
->fsq_next
;
610 free(fsqp
->fsq_devname
);
616 getdiskquota(fsqp
, uid
, dqp
)
622 char qfilename
[MAXPATHLEN
];
624 snprintf(qfilename
, sizeof (qfilename
), "%s/%s", fsqp
->fsq_dir
, QFNAME
);
625 if ((fd
= open64(qfilename
, O_RDONLY
)) < 0)
627 (void) llseek(fd
, (offset_t
)dqoff(uid
), L_SET
);
628 if (read(fd
, dqp
, sizeof (struct dqblk
)) != sizeof (struct dqblk
)) {
633 if (dqp
->dqb_bhardlimit
== 0 && dqp
->dqb_bsoftlimit
== 0 &&
634 dqp
->dqb_fhardlimit
== 0 && dqp
->dqb_fsoftlimit
== 0) {
641 * Get the client's hostname from the transport handle
642 * If the name is not available then return "(anon)".
644 struct nd_hostservlist
*
645 getclientsnames(transp
)
649 struct netconfig
*nconf
;
650 static struct nd_hostservlist
*serv
;
651 static struct nd_hostservlist anon_hsl
;
652 static struct nd_hostserv anon_hs
;
653 static char anon_hname
[] = "(anon)";
654 static char anon_sname
[] = "";
656 /* Set up anonymous client */
657 anon_hs
.h_host
= anon_hname
;
658 anon_hs
.h_serv
= anon_sname
;
660 anon_hsl
.h_hostservs
= &anon_hs
;
663 netdir_free((char *)serv
, ND_HOSTSERVLIST
);
666 nconf
= getnetconfigent(transp
->xp_netid
);
668 syslog(LOG_ERR
, "%s: getnetconfigent failed",
673 nbuf
= svc_getrpccaller(transp
);
675 freenetconfigent(nconf
);
678 if (netdir_getbyaddr(nconf
, &serv
, nbuf
)) {
679 freenetconfigent(nconf
);
682 freenetconfigent(nconf
);
687 log_cant_reply(transp
)
691 struct nd_hostservlist
*clnames
;
694 saverrno
= errno
; /* save error code */
695 clnames
= getclientsnames(transp
);
698 name
= clnames
->h_hostservs
->h_host
;
702 syslog(LOG_ERR
, "couldn't send reply to %s", name
);
704 syslog(LOG_ERR
, "couldn't send reply to %s: %m", name
);
707 char *mntopts
[] = { MNTOPT_QUOTA
, NULL
};
711 * Return 1 if "quota" appears in the options string
721 while (*opts
!= '\0') {
722 if (getsubopt(&opts
, mntopts
, &value
) == QUOTA
)