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(); 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(cmd, mountp, uid, dqp) 410 int cmd; 411 char *mountp; 412 uid_t uid; 413 struct dqblk *dqp; 414 { 415 int fd; 416 int status; 417 struct quotctl quota; 418 char mountpoint[256]; 419 FILE *fstab; 420 struct mnttab mntp; 421 422 if ((mountp == NULL) && (cmd == Q_ALLSYNC)) { 423 /* 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. 427 */ 428 if ((fstab = fopen(MNTTAB, "r")) == NULL) { 429 syslog(LOG_ERR, "can not open %s: %m ", MNTTAB); 430 return (-1); 431 } 432 fd = -1; 433 while ((status = getmntent(fstab, &mntp)) == NULL) { 434 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0 || 435 !(hasmntopt(&mntp, MNTOPT_RQ) || 436 hasmntopt(&mntp, MNTOPT_QUOTA))) 437 continue; 438 (void) strlcpy(mountpoint, mntp.mnt_mountp, 439 sizeof (mountpoint)); 440 strcat(mountpoint, "/quotas"); 441 if ((fd = open64(mountpoint, O_RDWR)) >= 0) 442 break; 443 } 444 fclose(fstab); 445 if (fd == -1) { 446 errno = ENOENT; 447 return (-1); 448 } 449 } else { 450 if (mountp == NULL || mountp[0] == '\0') { 451 errno = ENOENT; 452 return (-1); 453 } 454 (void) strlcpy(mountpoint, mountp, sizeof (mountpoint)); 455 strcat(mountpoint, "/quotas"); 456 457 if ((fd = open64(mountpoint, O_RDONLY)) < 0) { 458 errno = ENOENT; 459 syslog(LOG_ERR, "can not open %s: %m ", mountpoint); 460 return (-1); 461 } 462 } 463 quota.op = cmd; 464 quota.uid = uid; 465 quota.addr = (caddr_t)dqp; 466 467 status = ioctl(fd, Q_QUOTACTL, "a); 468 469 close(fd); 470 return (status); 471 } 472 473 /* 474 * Return the quota information for the given path. Returns NULL if none 475 * was found. 476 */ 477 478 struct fsquot * 479 findfsq(char *dir) 480 { 481 struct stat sb; 482 struct fsquot *fsqp; 483 static time_t lastmtime = 0; /* mount table's previous mtime */ 484 485 /* 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. 489 */ 490 491 if (stat(MNTTAB, &sb) < 0) { 492 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB); 493 return (NULL); 494 } 495 if (lastmtime == 0 || sb.st_mtime != lastmtime) { 496 freefs(); 497 setupfs(); 498 lastmtime = sb.st_mtime; 499 } 500 501 /* 502 * Try to find the given path in the list of file systems with 503 * quotas. 504 */ 505 506 if (fsqlist == NULL) 507 return (NULL); 508 if (stat(dir, &sb) < 0) 509 return (NULL); 510 511 for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) { 512 if (sb.st_dev == fsqp->fsq_dev) 513 return (fsqp); 514 } 515 516 return (NULL); 517 } 518 519 static void 520 setup_zfs(struct mnttab *mp) 521 { 522 struct fsquot *fsqp; 523 struct stat sb; 524 525 if (stat(mp->mnt_mountp, &sb) < 0) 526 return; 527 528 fsqp = malloc(sizeof (struct fsquot)); 529 if (fsqp == NULL) { 530 syslog(LOG_ERR, "out of memory"); 531 zexit(1); 532 } 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"); 537 zexit(1); 538 } 539 540 fsqp->fsq_fstype = MNTTYPE_ZFS; 541 fsqp->fsq_dev = sb.st_dev; 542 fsqp->fsq_next = fsqlist; 543 fsqlist = fsqp; 544 } 545 546 void 547 setupfs() 548 { 549 struct fsquot *fsqp; 550 FILE *mt; 551 struct mnttab m; 552 struct stat sb; 553 char qfilename[MAXPATHLEN]; 554 555 mt = fopen(MNTTAB, "r"); 556 if (mt == NULL) { 557 syslog(LOG_ERR, "can't read %s: %m", MNTTAB); 558 return; 559 } 560 561 while (getmntent(mt, &m) == 0) { 562 if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) { 563 setup_zfs(&m); 564 continue; 565 } 566 567 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0) 568 continue; 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) 573 continue; 574 } 575 if (stat(m.mnt_special, &sb) < 0 || 576 (sb.st_mode & S_IFMT) != S_IFBLK) 577 continue; 578 fsqp = malloc(sizeof (struct fsquot)); 579 if (fsqp == NULL) { 580 syslog(LOG_ERR, "out of memory"); 581 zexit(1); 582 } 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"); 587 zexit(1); 588 } 589 fsqp->fsq_fstype = MNTTYPE_UFS; 590 fsqp->fsq_dev = sb.st_rdev; 591 fsqp->fsq_next = fsqlist; 592 fsqlist = fsqp; 593 } 594 (void) fclose(mt); 595 } 596 597 /* 598 * Free the memory used by the current list of quota'd file systems. Nulls 599 * out the list. 600 */ 601 602 void 603 freefs() 604 { 605 register struct fsquot *fsqp; 606 607 while ((fsqp = fsqlist) != NULL) { 608 fsqlist = fsqp->fsq_next; 609 free(fsqp->fsq_dir); 610 free(fsqp->fsq_devname); 611 free(fsqp); 612 } 613 } 614 615 int 616 getdiskquota(fsqp, uid, dqp) 617 struct fsquot *fsqp; 618 uid_t uid; 619 struct dqblk *dqp; 620 { 621 int fd; 622 char qfilename[MAXPATHLEN]; 623 624 snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME); 625 if ((fd = open64(qfilename, O_RDONLY)) < 0) 626 return (0); 627 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 628 if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) { 629 close(fd); 630 return (0); 631 } 632 close(fd); 633 if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 && 634 dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) { 635 return (0); 636 } 637 return (1); 638 } 639 640 /* 641 * Get the client's hostname from the transport handle 642 * If the name is not available then return "(anon)". 643 */ 644 struct nd_hostservlist * 645 getclientsnames(transp) 646 SVCXPRT *transp; 647 { 648 struct netbuf *nbuf; 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[] = ""; 655 656 /* Set up anonymous client */ 657 anon_hs.h_host = anon_hname; 658 anon_hs.h_serv = anon_sname; 659 anon_hsl.h_cnt = 1; 660 anon_hsl.h_hostservs = &anon_hs; 661 662 if (serv) { 663 netdir_free((char *)serv, ND_HOSTSERVLIST); 664 serv = NULL; 665 } 666 nconf = getnetconfigent(transp->xp_netid); 667 if (nconf == NULL) { 668 syslog(LOG_ERR, "%s: getnetconfigent failed", 669 transp->xp_netid); 670 return (&anon_hsl); 671 } 672 673 nbuf = svc_getrpccaller(transp); 674 if (nbuf == NULL) { 675 freenetconfigent(nconf); 676 return (&anon_hsl); 677 } 678 if (netdir_getbyaddr(nconf, &serv, nbuf)) { 679 freenetconfigent(nconf); 680 return (&anon_hsl); 681 } 682 freenetconfigent(nconf); 683 return (serv); 684 } 685 686 void 687 log_cant_reply(transp) 688 SVCXPRT *transp; 689 { 690 int saverrno; 691 struct nd_hostservlist *clnames; 692 register char *name; 693 694 saverrno = errno; /* save error code */ 695 clnames = getclientsnames(transp); 696 if (clnames == NULL) 697 return; 698 name = clnames->h_hostservs->h_host; 699 700 errno = saverrno; 701 if (errno == 0) 702 syslog(LOG_ERR, "couldn't send reply to %s", name); 703 else 704 syslog(LOG_ERR, "couldn't send reply to %s: %m", name); 705 } 706 707 char *mntopts[] = { MNTOPT_QUOTA, NULL }; 708 #define QUOTA 0 709 710 /* 711 * Return 1 if "quota" appears in the options string 712 */ 713 int 714 hasquota(opts) 715 char *opts; 716 { 717 char *value; 718 719 if (opts == NULL) 720 return (0); 721 while (*opts != '\0') { 722 if (getsubopt(&opts, mntopts, &value) == QUOTA) 723 return (1); 724 } 725 726 return (0); 727 } 728 729 static void 730 zexit(int n) 731 { 732 if (g_zfs != NULL) 733 _libzfs_fini(g_zfs); 734 exit(n); 735 } 736