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