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(dir) 477 char *dir; 478 { 479 struct stat sb; 480 register struct fsquot *fsqp; 481 static time_t lastmtime = 0; /* mount table's previous mtime */ 482 483 /* 484 * If we've never looked at the mount table, or it has changed 485 * since the last time, rebuild the list of quota'd file systems 486 * and remember the current mod time for the mount table. 487 */ 488 489 if (stat(MNTTAB, &sb) < 0) { 490 syslog(LOG_ERR, "can't stat %s: %m", MNTTAB); 491 return (NULL); 492 } 493 if (lastmtime == 0 || sb.st_mtime != lastmtime) { 494 freefs(); 495 setupfs(); 496 lastmtime = sb.st_mtime; 497 } 498 499 /* 500 * Try to find the given path in the list of file systems with 501 * quotas. 502 */ 503 504 if (fsqlist == NULL) 505 return (NULL); 506 if (stat(dir, &sb) < 0) 507 return (NULL); 508 for (fsqp = fsqlist; fsqp != NULL; fsqp = fsqp->fsq_next) { 509 if (strcmp(fsqp->fsq_fstype, MNTTYPE_ZFS) == 0) { 510 if (strcmp(fsqp->fsq_dir, dir) == 0) 511 return (fsqp); 512 } else 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 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 fsqp->fsq_fstype = MNTTYPE_ZFS; 536 fsqp->fsq_next = fsqlist; 537 fsqlist = fsqp; 538 } 539 540 void 541 setupfs() 542 { 543 struct fsquot *fsqp; 544 FILE *mt; 545 struct mnttab m; 546 struct stat sb; 547 char qfilename[MAXPATHLEN]; 548 549 mt = fopen(MNTTAB, "r"); 550 if (mt == NULL) { 551 syslog(LOG_ERR, "can't read %s: %m", MNTTAB); 552 return; 553 } 554 555 while (getmntent(mt, &m) == 0) { 556 if (strcmp(m.mnt_fstype, MNTTYPE_ZFS) == 0) { 557 setup_zfs(&m); 558 continue; 559 } 560 561 if (strcmp(m.mnt_fstype, MNTTYPE_UFS) != 0) 562 continue; 563 if (!hasquota(m.mnt_mntopts)) { 564 snprintf(qfilename, sizeof (qfilename), "%s/%s", 565 m.mnt_mountp, QFNAME); 566 if (access(qfilename, F_OK) < 0) 567 continue; 568 } 569 if (stat(m.mnt_special, &sb) < 0 || 570 (sb.st_mode & S_IFMT) != S_IFBLK) 571 continue; 572 fsqp = malloc(sizeof (struct fsquot)); 573 if (fsqp == NULL) { 574 syslog(LOG_ERR, "out of memory"); 575 zexit(1); 576 } 577 fsqp->fsq_dir = strdup(m.mnt_mountp); 578 fsqp->fsq_devname = strdup(m.mnt_special); 579 if (fsqp->fsq_dir == NULL || fsqp->fsq_devname == NULL) { 580 syslog(LOG_ERR, "out of memory"); 581 zexit(1); 582 } 583 fsqp->fsq_fstype = MNTTYPE_UFS; 584 fsqp->fsq_dev = sb.st_rdev; 585 fsqp->fsq_next = fsqlist; 586 fsqlist = fsqp; 587 } 588 (void) fclose(mt); 589 } 590 591 /* 592 * Free the memory used by the current list of quota'd file systems. Nulls 593 * out the list. 594 */ 595 596 void 597 freefs() 598 { 599 register struct fsquot *fsqp; 600 601 while ((fsqp = fsqlist) != NULL) { 602 fsqlist = fsqp->fsq_next; 603 free(fsqp->fsq_dir); 604 free(fsqp->fsq_devname); 605 free(fsqp); 606 } 607 } 608 609 int 610 getdiskquota(fsqp, uid, dqp) 611 struct fsquot *fsqp; 612 uid_t uid; 613 struct dqblk *dqp; 614 { 615 int fd; 616 char qfilename[MAXPATHLEN]; 617 618 snprintf(qfilename, sizeof (qfilename), "%s/%s", fsqp->fsq_dir, QFNAME); 619 if ((fd = open64(qfilename, O_RDONLY)) < 0) 620 return (0); 621 (void) llseek(fd, (offset_t)dqoff(uid), L_SET); 622 if (read(fd, dqp, sizeof (struct dqblk)) != sizeof (struct dqblk)) { 623 close(fd); 624 return (0); 625 } 626 close(fd); 627 if (dqp->dqb_bhardlimit == 0 && dqp->dqb_bsoftlimit == 0 && 628 dqp->dqb_fhardlimit == 0 && dqp->dqb_fsoftlimit == 0) { 629 return (0); 630 } 631 return (1); 632 } 633 634 /* 635 * Get the client's hostname from the transport handle 636 * If the name is not available then return "(anon)". 637 */ 638 struct nd_hostservlist * 639 getclientsnames(transp) 640 SVCXPRT *transp; 641 { 642 struct netbuf *nbuf; 643 struct netconfig *nconf; 644 static struct nd_hostservlist *serv; 645 static struct nd_hostservlist anon_hsl; 646 static struct nd_hostserv anon_hs; 647 static char anon_hname[] = "(anon)"; 648 static char anon_sname[] = ""; 649 650 /* Set up anonymous client */ 651 anon_hs.h_host = anon_hname; 652 anon_hs.h_serv = anon_sname; 653 anon_hsl.h_cnt = 1; 654 anon_hsl.h_hostservs = &anon_hs; 655 656 if (serv) { 657 netdir_free((char *)serv, ND_HOSTSERVLIST); 658 serv = NULL; 659 } 660 nconf = getnetconfigent(transp->xp_netid); 661 if (nconf == NULL) { 662 syslog(LOG_ERR, "%s: getnetconfigent failed", 663 transp->xp_netid); 664 return (&anon_hsl); 665 } 666 667 nbuf = svc_getrpccaller(transp); 668 if (nbuf == NULL) { 669 freenetconfigent(nconf); 670 return (&anon_hsl); 671 } 672 if (netdir_getbyaddr(nconf, &serv, nbuf)) { 673 freenetconfigent(nconf); 674 return (&anon_hsl); 675 } 676 freenetconfigent(nconf); 677 return (serv); 678 } 679 680 void 681 log_cant_reply(transp) 682 SVCXPRT *transp; 683 { 684 int saverrno; 685 struct nd_hostservlist *clnames; 686 register char *name; 687 688 saverrno = errno; /* save error code */ 689 clnames = getclientsnames(transp); 690 if (clnames == NULL) 691 return; 692 name = clnames->h_hostservs->h_host; 693 694 errno = saverrno; 695 if (errno == 0) 696 syslog(LOG_ERR, "couldn't send reply to %s", name); 697 else 698 syslog(LOG_ERR, "couldn't send reply to %s: %m", name); 699 } 700 701 char *mntopts[] = { MNTOPT_QUOTA, NULL }; 702 #define QUOTA 0 703 704 /* 705 * Return 1 if "quota" appears in the options string 706 */ 707 int 708 hasquota(opts) 709 char *opts; 710 { 711 char *value; 712 713 if (opts == NULL) 714 return (0); 715 while (*opts != '\0') { 716 if (getsubopt(&opts, mntopts, &value) == QUOTA) 717 return (1); 718 } 719 720 return (0); 721 } 722 723 static void 724 zexit(int n) 725 { 726 if (g_zfs != NULL) 727 _libzfs_fini(g_zfs); 728 exit(n); 729 } 730