1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2017 Joyent Inc 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <signal.h> 32 #include <syslog.h> 33 #include <string.h> 34 #include <stropts.h> 35 #include <errno.h> 36 #include <sys/netconfig.h> 37 #include <sys/mntent.h> 38 #include <sys/mnttab.h> 39 #include <sys/param.h> 40 #include <sys/time.h> 41 #include <sys/debug.h> 42 #ifdef notdef 43 #include <netconfig.h> 44 #endif 45 #include <sys/stat.h> 46 #include <sys/file.h> 47 #include <sys/fs/ufs_quota.h> 48 #include <netdir.h> 49 #include <rpc/rpc.h> 50 #include <rpcsvc/rquota.h> 51 #include <tiuser.h> 52 #include <unistd.h> 53 #include <dlfcn.h> 54 #include <libzfs.h> 55 56 #define QFNAME "quotas" /* name of quota file */ 57 #define RPCSVC_CLOSEDOWN 120 /* 2 minutes */ 58 59 struct fsquot { 60 char *fsq_fstype; 61 struct fsquot *fsq_next; 62 char *fsq_dir; 63 char *fsq_devname; 64 dev_t fsq_dev; 65 }; 66 67 struct fsquot *fsqlist = NULL; 68 69 typedef struct authunix_parms *authp; 70 71 static int request_pending; /* Request in progress ? */ 72 73 void closedown(); 74 void dispatch(); 75 struct fsquot *findfsq(); 76 void freefs(); 77 int getdiskquota(); 78 void getquota(); 79 int hasquota(); 80 void log_cant_reply(); 81 void setupfs(); 82 static void zexit(int) __NORETURN; 83 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 *, 89 uint64_t *); 90 static libzfs_handle_t *g_zfs = NULL; 91 92 /* 93 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs 94 * packages. 'rquotad' supports zfs as an option. 95 */ 96 static void 97 load_libzfs(void) 98 { 99 void *hdl; 100 101 if (g_zfs != NULL) 102 return; 103 104 if ((hdl = dlopen("libzfs.so", RTLD_LAZY)) != NULL) { 105 _libzfs_init = (libzfs_handle_t *(*)(void))dlsym(hdl, 106 "libzfs_init"); 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"); 112 113 if (_libzfs_init && _libzfs_fini && _zfs_open && 114 _zfs_close && _zfs_prop_get_userquota_int) 115 g_zfs = _libzfs_init(); 116 } 117 } 118 119 /*ARGSUSED*/ 120 int 121 main(int argc, char *argv[]) 122 { 123 register SVCXPRT *transp; 124 125 load_libzfs(); 126 127 /* 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 131 * TLI endpoint. 132 */ 133 if (t_getstate(0) != -1 || t_errno != TBADF) { 134 char *netid; 135 struct netconfig *nconf = NULL; 136 137 openlog("rquotad", LOG_PID, LOG_DAEMON); 138 139 if ((netid = getenv("NLSPROVIDER")) == NULL) { 140 struct t_info tinfo; 141 142 if (t_sync(0) == -1) { 143 syslog(LOG_ERR, "could not do t_sync"); 144 zexit(1); 145 } 146 if (t_getinfo(0, &tinfo) == -1) { 147 syslog(LOG_ERR, "t_getinfo failed"); 148 zexit(1); 149 } 150 if (tinfo.servtype == T_CLTS) { 151 if (tinfo.addr == INET_ADDRSTRLEN) 152 netid = "udp"; 153 else 154 netid = "udp6"; 155 } else { 156 syslog(LOG_ERR, "wrong transport"); 157 zexit(1); 158 } 159 } 160 if ((nconf = getnetconfigent(netid)) == NULL) { 161 syslog(LOG_ERR, "cannot get transport info"); 162 } 163 164 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { 165 syslog(LOG_ERR, "cannot create server handle"); 166 zexit(1); 167 } 168 if (nconf) 169 freenetconfigent(nconf); 170 171 if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, dispatch, 0)) { 172 syslog(LOG_ERR, 173 "unable to register (RQUOTAPROG, RQUOTAVERS)."); 174 zexit(1); 175 } 176 177 (void) sigset(SIGALRM, (void(*)(int)) closedown); 178 (void) alarm(RPCSVC_CLOSEDOWN); 179 180 svc_run(); 181 zexit(1); 182 /* NOTREACHED */ 183 } 184 185 /* 186 * Started from a shell - fork the daemon. 187 */ 188 189 switch (fork()) { 190 case 0: /* child */ 191 break; 192 case -1: 193 perror("rquotad: can't fork"); 194 zexit(1); 195 default: /* parent */ 196 zexit(0); 197 } 198 199 /* 200 * Close existing file descriptors, open "/dev/null" as 201 * standard input, output, and error, and detach from 202 * controlling terminal. 203 */ 204 closefrom(0); 205 (void) open("/dev/null", O_RDONLY); 206 (void) open("/dev/null", O_WRONLY); 207 (void) dup(1); 208 (void) setsid(); 209 210 openlog("rquotad", LOG_PID, LOG_DAEMON); 211 212 /* 213 * Create datagram service 214 */ 215 if (svc_create(dispatch, RQUOTAPROG, RQUOTAVERS, "datagram_v") == 0) { 216 syslog(LOG_ERR, "couldn't register datagram_v service"); 217 zexit(1); 218 } 219 220 /* 221 * Start serving 222 */ 223 svc_run(); 224 syslog(LOG_ERR, "Error: svc_run shouldn't have returned"); 225 return (1); 226 } 227 228 void 229 dispatch(rqstp, transp) 230 register struct svc_req *rqstp; 231 register SVCXPRT *transp; 232 { 233 234 request_pending = 1; 235 236 switch (rqstp->rq_proc) { 237 case NULLPROC: 238 errno = 0; 239 if (!svc_sendreply(transp, xdr_void, 0)) 240 log_cant_reply(transp); 241 break; 242 243 case RQUOTAPROC_GETQUOTA: 244 case RQUOTAPROC_GETACTIVEQUOTA: 245 getquota(rqstp, transp); 246 break; 247 248 default: 249 svcerr_noproc(transp); 250 break; 251 } 252 253 request_pending = 0; 254 } 255 256 void 257 closedown() 258 { 259 if (!request_pending) { 260 int i, openfd; 261 struct t_info tinfo; 262 263 if (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS)) 264 zexit(0); 265 266 for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) { 267 if (svc_pollfd[i].fd >= 0) 268 openfd++; 269 } 270 271 if (openfd <= 1) 272 zexit(0); 273 } 274 (void) alarm(RPCSVC_CLOSEDOWN); 275 } 276 277 static int 278 getzfsquota(uid_t user, char *dataset, struct dqblk *zq) 279 { 280 zfs_handle_t *zhp = NULL; 281 char propname[ZFS_MAXPROPLEN]; 282 uint64_t userquota, userused; 283 284 if (g_zfs == NULL) 285 return (1); 286 287 if ((zhp = _zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL) { 288 syslog(LOG_ERR, "can not open zfs dataset %s", dataset); 289 return (1); 290 } 291 292 (void) snprintf(propname, sizeof (propname), "userquota@%u", user); 293 if (_zfs_prop_get_userquota_int(zhp, propname, &userquota) != 0) { 294 _zfs_close(zhp); 295 return (1); 296 } 297 298 (void) snprintf(propname, sizeof (propname), "userused@%u", user); 299 if (_zfs_prop_get_userquota_int(zhp, propname, &userused) != 0) { 300 _zfs_close(zhp); 301 return (1); 302 } 303 304 zq->dqb_bhardlimit = userquota / DEV_BSIZE; 305 zq->dqb_bsoftlimit = userquota / DEV_BSIZE; 306 zq->dqb_curblocks = userused / DEV_BSIZE; 307 _zfs_close(zhp); 308 return (0); 309 } 310 311 void 312 getquota(rqstp, transp) 313 register struct svc_req *rqstp; 314 register SVCXPRT *transp; 315 { 316 struct getquota_args gqa; 317 struct getquota_rslt gqr; 318 struct dqblk dqblk; 319 struct fsquot *fsqp; 320 struct timeval tv; 321 bool_t qactive; 322 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); 326 return; 327 } 328 /* 329 * This authentication is really bogus with the current rpc 330 * authentication scheme. One day we will have something for real. 331 */ 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; 337 goto sendreply; 338 } 339 fsqp = findfsq(gqa.gqa_pathp); 340 if (fsqp == NULL) { 341 gqr.status = Q_NOQUOTA; 342 goto sendreply; 343 } 344 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; 349 goto sendreply; 350 } 351 qactive = TRUE; 352 } else { 353 if (quotactl(Q_GETQUOTA, fsqp->fsq_dir, 354 (uid_t)gqa.gqa_uid, &dqblk) != 0) { 355 qactive = FALSE; 356 if ((errno == ENOENT) || 357 (rqstp->rq_proc != RQUOTAPROC_GETQUOTA)) { 358 gqr.status = Q_NOQUOTA; 359 goto sendreply; 360 } 361 362 /* 363 * If there is no quotas file, don't bother to sync it. 364 */ 365 if (errno != ENOENT) { 366 if (quotactl(Q_ALLSYNC, fsqp->fsq_dir, 367 (uid_t)gqa.gqa_uid, &dqblk) < 0 && 368 errno == EINVAL) 369 syslog(LOG_WARNING, 370 "Quotas are not compiled " 371 "into this kernel"); 372 if (getdiskquota(fsqp, (uid_t)gqa.gqa_uid, 373 &dqblk) == 0) { 374 gqr.status = Q_NOQUOTA; 375 goto sendreply; 376 } 377 } 378 } else { 379 qactive = TRUE; 380 } 381 /* 382 * We send the remaining time instead of the absolute time 383 * because clock skew between machines should be much greater 384 * than rpc delay. 385 */ 386 #define gqrslt getquota_rslt_u.gqr_rquota 387 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; 391 } 392 393 gqr.status = Q_OK; 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; 402 sendreply: 403 errno = 0; 404 if (!svc_sendreply(transp, xdr_getquota_rslt, (caddr_t)&gqr)) 405 log_cant_reply(transp); 406 } 407 408 int 409 quotactl(int cmd, char *mountp, uid_t uid, struct dqblk *dqp) 410 { 411 int fd; 412 int status; 413 struct quotctl quota; 414 char mountpoint[256]; 415 FILE *fstab; 416 struct mnttab mntp; 417 418 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) { 419 /* 420 * Find the mount point of any ufs file system. this is 421 * because the ioctl that implements the quotactl call has 422 * to go to a real file, and not to the block device. 423 */ 424 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 425 syslog(LOG_ERR, "can not open %s: %m ", MNTTAB); 426 return (-1); 427 } 428 fd = -1; 429 while ((status = getmntent(fstab, &mntp)) == 0) { 430 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 || 431 !(hasmntopt(&mntp, MNTOPT_RQ) || 432 hasmntopt(&mntp, MNTOPT_QUOTA))) 433 continue; 434 (void) strlcpy(mountpoint, mntp.mnt_mountp, 435 sizeof (mountpoint)); 436 strcat(mountpoint, "/quotas"); 437 if ((fd = open64(mountpoint, O_RDWR)) >= 0) 438 break; 439 } 440 fclose(fstab); 441 if (fd == -1) { 442 errno = ENOENT; 443 return (-1); 444 } 445 } else { 446 if (mountp == NULL || mountp[0] == '\0') { 447 errno = ENOENT; 448 return (-1); 449 } 450 (void) strlcpy(mountpoint, mountp, sizeof (mountpoint)); 451 strcat(mountpoint, "/quotas"); 452 453 if ((fd = open64(mountpoint, O_RDONLY)) < 0) { 454 errno = ENOENT; 455 syslog(LOG_ERR, "can not open %s: %m ", mountpoint); 456 return (-1); 457 } 458 } 459 quota.op = cmd; 460 quota.uid = uid; 461 quota.addr = (caddr_t)dqp; 462 463 status = ioctl(fd, Q_QUOTACTL, "a); 464 465 close(fd); 466 return (status); 467 } 468 469 /* 470 * Return the quota information for the given path. Returns NULL if none 471 * was found. 472 */ 473 474 struct fsquot * 475 findfsq(char *dir) 476 { 477 struct stat sb; 478 struct fsquot *fsqp; 479 static time_t lastmtime = 0; /* mount table's previous mtime */ 480 481 /* 482 * If we've never looked at the mount table, or it has changed 483 * since the last time, rebuild the list of quota'd file systems 484 * and remember the current mod time for the mount table. 485 */ 486 487 if (stat(MNTTAB, &sb) < 0) { 488 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB); 489 return (NULL); 490 } 491 if (lastmtime == 0 || sb.st_mtime != lastmtime) { 492 freefs(); 493 setupfs(); 494 lastmtime = sb.st_mtime; 495 } 496 497 /* 498 * Try to find the given path in the list of file systems with 499 * quotas. 500 */ 501 502 if (fsqlist == NULL) 503 return (NULL); 504 if (stat(dir, &sb) < 0) 505 return (NULL); 506 507 for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) { 508 if (sb.st_dev == fsqp->fsq_dev) 509 return (fsqp); 510 } 511 512 return (NULL); 513 } 514 515 static void 516 setup_zfs(struct mnttab *mp) 517 { 518 struct fsquot *fsqp; 519 struct stat sb; 520 521 if (stat(mp->mnt_mountp, &sb) < 0) 522 return; 523 524 fsqp = malloc(sizeof (struct fsquot)); 525 if (fsqp == NULL) { 526 syslog(LOG_ERR, "out of memory"); 527 zexit(1); 528 } 529 fsqp->fsq_dir = strdup(mp->mnt_mountp); 530 fsqp->fsq_devname = strdup(mp->mnt_special); 531 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) { 532 syslog(LOG_ERR, "out of memory"); 533 zexit(1); 534 } 535 536 fsqp->fsq_fstype = MNTTYPE_ZFS; 537 fsqp->fsq_dev = sb.st_dev; 538 fsqp->fsq_next = fsqlist; 539 fsqlist = fsqp; 540 } 541 542 void 543 setupfs() 544 { 545 struct fsquot *fsqp; 546 FILE *mt; 547 struct mnttab m; 548 struct stat sb; 549 char qfilename[MAXPATHLEN]; 550 551 mt = fopen(MNTTAB, "r"); 552 if (mt == NULL) { 553 syslog(LOG_ERR, "can't read %s: %m", MNTTAB); 554 return; 555 } 556 557 while (getmntent(mt, &m) == 0) { 558 if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) { 559 setup_zfs(&m); 560 continue; 561 } 562 563 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0) 564 continue; 565 if (!hasquota(m.mnt_mntopts)) { 566 snprintf(qfilename, sizeof (qfilename), "%s/%s", 567 m.mnt_mountp, QFNAME); 568 if (access(qfilename, F_OK) < 0) 569 continue; 570 } 571 if (stat(m.mnt_special, &sb) < 0 || 572 (sb.st_mode & S_IFMT) != S_IFBLK) 573 continue; 574 fsqp = malloc(sizeof (struct fsquot)); 575 if (fsqp == NULL) { 576 syslog(LOG_ERR, "out of memory"); 577 zexit(1); 578 } 579 fsqp->fsq_dir = strdup(m.mnt_mountp); 580 fsqp->fsq_devname = strdup(m.mnt_special); 581 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) { 582 syslog(LOG_ERR, "out of memory"); 583 zexit(1); 584 } 585 fsqp->fsq_fstype = MNTTYPE_UFS; 586 fsqp->fsq_dev = sb.st_rdev; 587 fsqp->fsq_next = fsqlist; 588 fsqlist = fsqp; 589 } 590 (void) fclose(mt); 591 } 592 593 /* 594 * Free the memory used by the current list of quota'd file systems. Nulls 595 * out the list. 596 */ 597 598 void 599 freefs() 600 { 601 register struct fsquot *fsqp; 602 603 while ((fsqp = fsqlist) != NULL) { 604 fsqlist = fsqp->fsq_next; 605 free(fsqp->fsq_dir); 606 free(fsqp->fsq_devname); 607 free(fsqp); 608 } 609 } 610 611 int 612 getdiskquota(fsqp, uid, dqp) 613 struct fsquot *fsqp; 614 uid_t uid; 615 struct dqblk *dqp; 616 { 617 int fd; 618 char qfilename[MAXPATHLEN]; 619 620 snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME); 621 if ((fd = open64(qfilename, O_RDONLY)) < 0) 622 return (0); 623 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 624 if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) { 625 close(fd); 626 return (0); 627 } 628 close(fd); 629 if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 && 630 dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) { 631 return (0); 632 } 633 return (1); 634 } 635 636 /* 637 * Get the client's hostname from the transport handle 638 * If the name is not available then return "(anon)". 639 */ 640 struct nd_hostservlist * 641 getclientsnames(transp) 642 SVCXPRT *transp; 643 { 644 struct netbuf *nbuf; 645 struct netconfig *nconf; 646 static struct nd_hostservlist *serv; 647 static struct nd_hostservlist anon_hsl; 648 static struct nd_hostserv anon_hs; 649 static char anon_hname[] = "(anon)"; 650 static char anon_sname[] = ""; 651 652 /* Set up anonymous client */ 653 anon_hs.h_host = anon_hname; 654 anon_hs.h_serv = anon_sname; 655 anon_hsl.h_cnt = 1; 656 anon_hsl.h_hostservs = &anon_hs; 657 658 if (serv) { 659 netdir_free((char *)serv, ND_HOSTSERVLIST); 660 serv = NULL; 661 } 662 nconf = getnetconfigent(transp->xp_netid); 663 if (nconf == NULL) { 664 syslog(LOG_ERR, "%s: getnetconfigent failed", 665 transp->xp_netid); 666 return (&anon_hsl); 667 } 668 669 nbuf = svc_getrpccaller(transp); 670 if (nbuf == NULL) { 671 freenetconfigent(nconf); 672 return (&anon_hsl); 673 } 674 if (netdir_getbyaddr(nconf, &serv, nbuf)) { 675 freenetconfigent(nconf); 676 return (&anon_hsl); 677 } 678 freenetconfigent(nconf); 679 return (serv); 680 } 681 682 void 683 log_cant_reply(transp) 684 SVCXPRT *transp; 685 { 686 int saverrno; 687 struct nd_hostservlist *clnames; 688 register char *name; 689 690 saverrno = errno; /* save error code */ 691 clnames = getclientsnames(transp); 692 if (clnames == NULL) 693 return; 694 name = clnames->h_hostservs->h_host; 695 696 errno = saverrno; 697 if (errno == 0) 698 syslog(LOG_ERR, "couldn't send reply to %s", name); 699 else 700 syslog(LOG_ERR, "couldn't send reply to %s: %m", name); 701 } 702 703 char *mntopts[] = { MNTOPT_QUOTA, NULL }; 704 #define QUOTA 0 705 706 /* 707 * Return 1 if "quota" appears in the options string 708 */ 709 int 710 hasquota(opts) 711 char *opts; 712 { 713 char *value; 714 715 if (opts == NULL) 716 return (0); 717 while (*opts != '\0') { 718 if (getsubopt(&opts, mntopts, &value) == QUOTA) 719 return (1); 720 } 721 722 return (0); 723 } 724 725 static void 726 zexit(int n) 727 { 728 if (g_zfs != NULL) 729 _libzfs_fini(g_zfs); 730 exit(n); 731 } 732