1/* 2 Unix SMB/CIFS implementation. 3 support for quotas 4 Copyright (C) Andrew Tridgell 1992-1998 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*/ 20 21 22/* 23 * This is one of the most system dependent parts of Samba, and its 24 * done a litle differently. Each system has its own way of doing 25 * things :-( 26 */ 27 28#include "includes.h" 29 30#undef DBGC_CLASS 31#define DBGC_CLASS DBGC_QUOTA 32 33#ifndef HAVE_SYS_QUOTAS 34 35/* just a quick hack because sysquotas.h is included before linux/quota.h */ 36#ifdef QUOTABLOCK_SIZE 37#undef QUOTABLOCK_SIZE 38#endif 39 40#ifdef WITH_QUOTAS 41 42#if defined(VXFS_QUOTA) 43 44/* 45 * In addition to their native filesystems, some systems have Veritas VxFS. 46 * Declare here, define at end: reduces likely "include" interaction problems. 47 * David Lee <T.D.Lee@durham.ac.uk> 48 */ 49BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize); 50 51#endif /* VXFS_QUOTA */ 52 53#ifdef LINUX 54 55#include <sys/types.h> 56#include <mntent.h> 57 58/* 59 * This shouldn't be neccessary - it should be /usr/include/sys/quota.h 60 * So we include all the files has *should* be in the system into a large, 61 * grungy samba_linux_quoatas.h Sometimes I *hate* Linux :-). JRA. 62 */ 63 64#include "samba_linux_quota.h" 65#include "samba_xfs_quota.h" 66 67typedef struct _LINUX_SMB_DISK_QUOTA { 68 SMB_BIG_UINT bsize; 69 SMB_BIG_UINT hardlimit; /* In bsize units. */ 70 SMB_BIG_UINT softlimit; /* In bsize units. */ 71 SMB_BIG_UINT curblocks; /* In bsize units. */ 72 SMB_BIG_UINT ihardlimit; /* inode hard limit. */ 73 SMB_BIG_UINT isoftlimit; /* inode soft limit. */ 74 SMB_BIG_UINT curinodes; /* Current used inodes. */ 75} LINUX_SMB_DISK_QUOTA; 76 77/**************************************************************************** 78 Abstract out the XFS Quota Manager quota get call. 79****************************************************************************/ 80 81static int get_smb_linux_xfs_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp) 82{ 83 struct fs_disk_quota D; 84 int ret; 85 86 ZERO_STRUCT(D); 87 88 ret = quotactl(QCMD(Q_XGETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D); 89 90 if (ret) 91 ret = quotactl(QCMD(Q_XGETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D); 92 93 if (ret) 94 return ret; 95 96 dp->bsize = (SMB_BIG_UINT)512; 97 dp->softlimit = (SMB_BIG_UINT)D.d_blk_softlimit; 98 dp->hardlimit = (SMB_BIG_UINT)D.d_blk_hardlimit; 99 dp->ihardlimit = (SMB_BIG_UINT)D.d_ino_hardlimit; 100 dp->isoftlimit = (SMB_BIG_UINT)D.d_ino_softlimit; 101 dp->curinodes = (SMB_BIG_UINT)D.d_icount; 102 dp->curblocks = (SMB_BIG_UINT)D.d_bcount; 103 104 return ret; 105} 106 107/**************************************************************************** 108 Abstract out the old and new Linux quota get calls. 109****************************************************************************/ 110 111static int get_smb_linux_v1_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp) 112{ 113 struct v1_kern_dqblk D; 114 int ret; 115 116 ZERO_STRUCT(D); 117 118 ret = quotactl(QCMD(Q_V1_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D); 119 120 if (ret && errno != EDQUOT) 121 ret = quotactl(QCMD(Q_V1_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D); 122 123 if (ret && errno != EDQUOT) 124 return ret; 125 126 dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE; 127 dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit; 128 dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit; 129 dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit; 130 dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit; 131 dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes; 132 dp->curblocks = (SMB_BIG_UINT)D.dqb_curblocks; 133 134 return ret; 135} 136 137static int get_smb_linux_v2_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp) 138{ 139 struct v2_kern_dqblk D; 140 int ret; 141 142 ZERO_STRUCT(D); 143 144 ret = quotactl(QCMD(Q_V2_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D); 145 146 if (ret && errno != EDQUOT) 147 ret = quotactl(QCMD(Q_V2_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D); 148 149 if (ret && errno != EDQUOT) 150 return ret; 151 152 dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE; 153 dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit; 154 dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit; 155 dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit; 156 dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit; 157 dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes; 158 dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize; 159 160 return ret; 161} 162 163/**************************************************************************** 164 Brand-new generic quota interface. 165****************************************************************************/ 166 167static int get_smb_linux_gen_quota(char *path, uid_t euser_id, gid_t egrp_id, LINUX_SMB_DISK_QUOTA *dp) 168{ 169 struct if_dqblk D; 170 int ret; 171 172 ZERO_STRUCT(D); 173 174 ret = quotactl(QCMD(Q_GETQUOTA,USRQUOTA), path, euser_id, (caddr_t)&D); 175 176 if (ret && errno != EDQUOT) 177 ret = quotactl(QCMD(Q_GETQUOTA,GRPQUOTA), path, egrp_id, (caddr_t)&D); 178 179 if (ret && errno != EDQUOT) 180 return ret; 181 182 dp->bsize = (SMB_BIG_UINT)QUOTABLOCK_SIZE; 183 dp->softlimit = (SMB_BIG_UINT)D.dqb_bsoftlimit; 184 dp->hardlimit = (SMB_BIG_UINT)D.dqb_bhardlimit; 185 dp->ihardlimit = (SMB_BIG_UINT)D.dqb_ihardlimit; 186 dp->isoftlimit = (SMB_BIG_UINT)D.dqb_isoftlimit; 187 dp->curinodes = (SMB_BIG_UINT)D.dqb_curinodes; 188 dp->curblocks = ((SMB_BIG_UINT)D.dqb_curspace) / dp->bsize; 189 190 return ret; 191} 192 193/**************************************************************************** 194 Try to get the disk space from disk quotas (LINUX version). 195****************************************************************************/ 196 197BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 198{ 199 int r; 200 SMB_STRUCT_STAT S; 201 FILE *fp; 202 LINUX_SMB_DISK_QUOTA D; 203 struct mntent *mnt; 204 SMB_DEV_T devno; 205 int found; 206 uid_t euser_id; 207 gid_t egrp_id; 208 209 euser_id = geteuid(); 210 egrp_id = getegid(); 211 212 /* find the block device file */ 213 214 if ( sys_stat(path, &S) == -1 ) 215 return(False) ; 216 217 devno = S.st_dev ; 218 219 fp = setmntent(MOUNTED,"r"); 220 found = False ; 221 222 while ((mnt = getmntent(fp))) { 223 if ( sys_stat(mnt->mnt_dir,&S) == -1 ) 224 continue ; 225 226 if (S.st_dev == devno) { 227 found = True ; 228 break; 229 } 230 } 231 232 endmntent(fp) ; 233 234 if (!found) 235 return(False); 236 237 save_re_uid(); 238 set_effective_uid(0); 239 240 if (strcmp(mnt->mnt_type, "xfs")==0) { 241 r=get_smb_linux_xfs_quota(mnt->mnt_fsname, euser_id, egrp_id, &D); 242 } else { 243 r=get_smb_linux_gen_quota(mnt->mnt_fsname, euser_id, egrp_id, &D); 244 if (r == -1 && errno != EDQUOT) { 245 r=get_smb_linux_v2_quota(mnt->mnt_fsname, euser_id, egrp_id, &D); 246 if (r == -1 && errno != EDQUOT) 247 r=get_smb_linux_v1_quota(mnt->mnt_fsname, euser_id, egrp_id, &D); 248 } 249 } 250 251 restore_re_uid(); 252 253 /* Use softlimit to determine disk space, except when it has been exceeded */ 254 *bsize = D.bsize; 255 if (r == -1) { 256 if (errno == EDQUOT) { 257 *dfree =0; 258 *dsize =D.curblocks; 259 return (True); 260 } else { 261 return(False); 262 } 263 } 264 265 /* Use softlimit to determine disk space, except when it has been exceeded */ 266 if ( 267 (D.softlimit && D.curblocks >= D.softlimit) || 268 (D.hardlimit && D.curblocks >= D.hardlimit) || 269 (D.isoftlimit && D.curinodes >= D.isoftlimit) || 270 (D.ihardlimit && D.curinodes>=D.ihardlimit) 271 ) { 272 *dfree = 0; 273 *dsize = D.curblocks; 274 } else if (D.softlimit==0 && D.hardlimit==0) { 275 return(False); 276 } else { 277 if (D.softlimit == 0) 278 D.softlimit = D.hardlimit; 279 *dfree = D.softlimit - D.curblocks; 280 *dsize = D.softlimit; 281 } 282 283 return (True); 284} 285 286#elif defined(CRAY) 287 288#include <sys/quota.h> 289#include <mntent.h> 290 291/**************************************************************************** 292try to get the disk space from disk quotas (CRAY VERSION) 293****************************************************************************/ 294 295BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 296{ 297 struct mntent *mnt; 298 FILE *fd; 299 SMB_STRUCT_STAT sbuf; 300 SMB_DEV_T devno ; 301 static SMB_DEV_T devno_cached = 0 ; 302 static pstring name; 303 struct q_request request ; 304 struct qf_header header ; 305 static int quota_default = 0 ; 306 int found ; 307 308 if ( sys_stat(path,&sbuf) == -1 ) 309 return(False) ; 310 311 devno = sbuf.st_dev ; 312 313 if ( devno != devno_cached ) { 314 315 devno_cached = devno ; 316 317 if ((fd = setmntent(KMTAB)) == NULL) 318 return(False) ; 319 320 found = False ; 321 322 while ((mnt = getmntent(fd)) != NULL) { 323 324 if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 ) 325 continue ; 326 327 if (sbuf.st_dev == devno) { 328 329 found = True ; 330 break ; 331 332 } 333 334 } 335 336 pstrcpy(name,mnt->mnt_dir) ; 337 endmntent(fd) ; 338 339 if ( ! found ) 340 return(False) ; 341 } 342 343 request.qf_magic = QF_MAGIC ; 344 request.qf_entry.id = geteuid() ; 345 346 if (quotactl(name, Q_GETQUOTA, &request) == -1) 347 return(False) ; 348 349 if ( ! request.user ) 350 return(False) ; 351 352 if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) { 353 354 if ( ! quota_default ) { 355 356 if ( quotactl(name, Q_GETHEADER, &header) == -1 ) 357 return(False) ; 358 else 359 quota_default = header.user_h.def_fq ; 360 } 361 362 *dfree = quota_default ; 363 364 }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) { 365 366 *dfree = 0 ; 367 368 }else{ 369 370 *dfree = request.qf_entry.user_q.f_quota ; 371 372 } 373 374 *dsize = request.qf_entry.user_q.f_use ; 375 376 if ( *dfree < *dsize ) 377 *dfree = 0 ; 378 else 379 *dfree -= *dsize ; 380 381 *bsize = 4096 ; /* Cray blocksize */ 382 383 return(True) ; 384 385} 386 387 388#elif defined(SUNOS5) || defined(SUNOS4) 389 390#include <fcntl.h> 391#include <sys/param.h> 392#if defined(SUNOS5) 393#include <sys/fs/ufs_quota.h> 394#include <sys/mnttab.h> 395#include <sys/mntent.h> 396#else /* defined(SUNOS4) */ 397#include <ufs/quota.h> 398#include <mntent.h> 399#endif 400 401#if defined(SUNOS5) 402 403/**************************************************************************** 404 Allows querying of remote hosts for quotas on NFS mounted shares. 405 Supports normal NFS and AMD mounts. 406 Alan Romeril <a.romeril@ic.ac.uk> July 2K. 407****************************************************************************/ 408 409#include <rpc/rpc.h> 410#include <rpc/types.h> 411#include <rpcsvc/rquota.h> 412#include <rpc/nettype.h> 413#include <rpc/xdr.h> 414 415static int quotastat; 416 417static int xdr_getquota_args(XDR *xdrsp, struct getquota_args *args) 418{ 419 if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN )) 420 return(0); 421 if (!xdr_int(xdrsp, &args->gqa_uid)) 422 return(0); 423 return (1); 424} 425 426static int xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr) 427{ 428 if (!xdr_int(xdrsp, "astat)) { 429 DEBUG(6,("nfs_quotas: Status bad or zero\n")); 430 return 0; 431 } 432 if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) { 433 DEBUG(6,("nfs_quotas: Block size bad or zero\n")); 434 return 0; 435 } 436 if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) { 437 DEBUG(6,("nfs_quotas: Active bad or zero\n")); 438 return 0; 439 } 440 if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) { 441 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n")); 442 return 0; 443 } 444 if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) { 445 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n")); 446 return 0; 447 } 448 if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) { 449 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n")); 450 return 0; 451 } 452 return (1); 453} 454 455/* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */ 456static BOOL nfs_quotas(char *nfspath, uid_t euser_id, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 457{ 458 uid_t uid = euser_id; 459 struct dqblk D; 460 char *mnttype = nfspath; 461 CLIENT *clnt; 462 struct getquota_rslt gqr; 463 struct getquota_args args; 464 char *cutstr, *pathname, *host, *testpath; 465 int len; 466 static struct timeval timeout = {2,0}; 467 enum clnt_stat clnt_stat; 468 BOOL ret = True; 469 470 *bsize = *dfree = *dsize = (SMB_BIG_UINT)0; 471 472 len=strcspn(mnttype, ":"); 473 pathname=strstr(mnttype, ":"); 474 cutstr = (char *) malloc(len+1); 475 if (!cutstr) 476 return False; 477 478 memset(cutstr, '\0', len+1); 479 host = strncat(cutstr,mnttype, sizeof(char) * len ); 480 DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr)); 481 DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype)); 482 testpath=strchr_m(mnttype, ':'); 483 args.gqa_pathp = testpath+1; 484 args.gqa_uid = uid; 485 486 DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp")); 487 488 if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) { 489 ret = False; 490 goto out; 491 } 492 493 clnt->cl_auth = authunix_create_default(); 494 DEBUG(9,("nfs_quotas: auth_success\n")); 495 496 clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args, (caddr_t)&args, xdr_getquota_rslt, (caddr_t)&gqr, timeout); 497 498 if (clnt_stat != RPC_SUCCESS) { 499 DEBUG(9,("nfs_quotas: clnt_call fail\n")); 500 ret = False; 501 goto out; 502 } 503 504 /* 505 * quotastat returns 0 if the rpc call fails, 1 if quotas exist, 2 if there is 506 * no quota set, and 3 if no permission to get the quota. If 0 or 3 return 507 * something sensible. 508 */ 509 510 switch ( quotastat ) { 511 case 0: 512 DEBUG(9,("nfs_quotas: Remote Quotas Failed! Error \"%i\" \n", quotastat )); 513 ret = False; 514 goto out; 515 516 case 1: 517 DEBUG(9,("nfs_quotas: Good quota data\n")); 518 D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit; 519 D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit; 520 D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks; 521 break; 522 523 case 2: 524 case 3: 525 D.dqb_bsoftlimit = 1; 526 D.dqb_curblocks = 1; 527 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", quotastat )); 528 break; 529 530 default: 531 DEBUG(9,("nfs_quotas: Remote Quotas Questionable! Error \"%i\" \n", quotastat )); 532 break; 533 } 534 535 DEBUG(10,("nfs_quotas: Let`s look at D a bit closer... status \"%i\" bsize \"%i\" active? \"%i\" bhard \"%i\" bsoft \"%i\" curb \"%i\" \n", 536 quotastat, 537 gqr.getquota_rslt_u.gqr_rquota.rq_bsize, 538 gqr.getquota_rslt_u.gqr_rquota.rq_active, 539 gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit, 540 gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit, 541 gqr.getquota_rslt_u.gqr_rquota.rq_curblocks)); 542 543 *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize; 544 *dsize = D.dqb_bsoftlimit; 545 546 if (D.dqb_curblocks == D.dqb_curblocks == 1) 547 *bsize = 512; 548 549 if (D.dqb_curblocks > D.dqb_bsoftlimit) { 550 *dfree = 0; 551 *dsize = D.dqb_curblocks; 552 } else 553 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; 554 555 out: 556 557 if (clnt) { 558 if (clnt->cl_auth) 559 auth_destroy(clnt->cl_auth); 560 clnt_destroy(clnt); 561 } 562 563 DEBUG(5,("nfs_quotas: For path \"%s\" returning bsize %llu, dfree %llu, dsize %llu\n",args.gqa_pathp,*bsize,*dfree,*dsize)); 564 565 SAFE_FREE(cutstr); 566 DEBUG(10,("nfs_quotas: End of nfs_quotas\n" )); 567 return ret; 568} 569#endif 570 571/**************************************************************************** 572try to get the disk space from disk quotas (SunOS & Solaris2 version) 573Quota code by Peter Urbanec (amiga@cse.unsw.edu.au). 574****************************************************************************/ 575 576BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 577{ 578 uid_t euser_id; 579 int ret; 580 struct dqblk D; 581#if defined(SUNOS5) 582 struct quotctl command; 583 int file; 584 static struct mnttab mnt; 585 static pstring name; 586 pstring devopt; 587#else /* SunOS4 */ 588 struct mntent *mnt; 589 static pstring name; 590#endif 591 FILE *fd; 592 SMB_STRUCT_STAT sbuf; 593 SMB_DEV_T devno ; 594 static SMB_DEV_T devno_cached = 0 ; 595 static int found ; 596 597 euser_id = geteuid(); 598 599 if ( sys_stat(path,&sbuf) == -1 ) 600 return(False) ; 601 602 devno = sbuf.st_dev ; 603 DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n", path,(unsigned int)devno)); 604 if ( devno != devno_cached ) { 605 devno_cached = devno ; 606#if defined(SUNOS5) 607 if ((fd = sys_fopen(MNTTAB, "r")) == NULL) 608 return(False) ; 609 610 found = False ; 611 slprintf(devopt, sizeof(devopt) - 1, "dev=%x", (unsigned int)devno); 612 while (getmntent(fd, &mnt) == 0) { 613 if( !hasmntopt(&mnt, devopt) ) 614 continue; 615 616 DEBUG(5,("disk_quotas: testing \"%s\" %s\n", mnt.mnt_mountp,devopt)); 617 618 /* quotas are only on vxfs, UFS or NFS */ 619 if ( strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 || 620 strcmp( mnt.mnt_fstype, "nfs" ) == 0 || 621 strcmp( mnt.mnt_fstype, "vxfs" ) == 0 ) { 622 found = True ; 623 break; 624 } 625 } 626 627 pstrcpy(name,mnt.mnt_mountp) ; 628 pstrcat(name,"/quotas") ; 629 fclose(fd) ; 630#else /* SunOS4 */ 631 if ((fd = setmntent(MOUNTED, "r")) == NULL) 632 return(False) ; 633 634 found = False ; 635 while ((mnt = getmntent(fd)) != NULL) { 636 if ( sys_stat(mnt->mnt_dir,&sbuf) == -1 ) 637 continue ; 638 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n", mnt->mnt_dir,(unsigned int)sbuf.st_dev)); 639 if (sbuf.st_dev == devno) { 640 found = True ; 641 break; 642 } 643 } 644 645 pstrcpy(name,mnt->mnt_fsname) ; 646 endmntent(fd) ; 647#endif 648 } 649 650 if ( ! found ) 651 return(False) ; 652 653 save_re_uid(); 654 set_effective_uid(0); 655 656#if defined(SUNOS5) 657 if ( strcmp( mnt.mnt_fstype, "nfs" ) == 0) { 658 BOOL retval; 659 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n", mnt.mnt_special)); 660 retval = nfs_quotas(mnt.mnt_special, euser_id, bsize, dfree, dsize); 661 restore_re_uid(); 662 return retval; 663 } 664 665 DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name)); 666 if((file=sys_open(name, O_RDONLY,0))<0) { 667 restore_re_uid(); 668 return(False); 669 } 670 command.op = Q_GETQUOTA; 671 command.uid = euser_id; 672 command.addr = (caddr_t) &D; 673 ret = ioctl(file, Q_QUOTACTL, &command); 674 close(file); 675#else 676 DEBUG(5,("disk_quotas: trying quotactl on device \"%s\"\n", name)); 677 ret = quotactl(Q_GETQUOTA, name, euser_id, &D); 678#endif 679 680 restore_re_uid(); 681 682 if (ret < 0) { 683 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n", strerror(errno) )); 684 685#if defined(SUNOS5) && defined(VXFS_QUOTA) 686 /* If normal quotactl() fails, try vxfs private calls */ 687 set_effective_uid(euser_id); 688 DEBUG(5,("disk_quotas: mount type \"%s\"\n", mnt.mnt_fstype)); 689 if ( 0 == strcmp ( mnt.mnt_fstype, "vxfs" )) { 690 BOOL retval; 691 retval = disk_quotas_vxfs(name, path, bsize, dfree, dsize); 692 return(retval); 693 } 694#else 695 return(False); 696#endif 697 } 698 699 /* If softlimit is zero, set it equal to hardlimit. 700 */ 701 702 if (D.dqb_bsoftlimit==0) 703 D.dqb_bsoftlimit = D.dqb_bhardlimit; 704 705 /* Use softlimit to determine disk space. A user exceeding the quota is told 706 * that there's no space left. Writes might actually work for a bit if the 707 * hardlimit is set higher than softlimit. Effectively the disk becomes 708 * made of rubber latex and begins to expand to accommodate the user :-) 709 */ 710 711 if (D.dqb_bsoftlimit==0) 712 return(False); 713 *bsize = DEV_BSIZE; 714 *dsize = D.dqb_bsoftlimit; 715 716 if (D.dqb_curblocks > D.dqb_bsoftlimit) { 717 *dfree = 0; 718 *dsize = D.dqb_curblocks; 719 } else 720 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; 721 722 DEBUG(5,("disk_quotas for path \"%s\" returning bsize %llu, dfree %llu, dsize %llu\n", 723 path,*bsize,*dfree,*dsize)); 724 725 return(True); 726} 727 728 729#elif defined(OSF1) 730#include <ufs/quota.h> 731 732/**************************************************************************** 733try to get the disk space from disk quotas - OSF1 version 734****************************************************************************/ 735 736BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 737{ 738 int r, save_errno; 739 struct dqblk D; 740 SMB_STRUCT_STAT S; 741 uid_t euser_id; 742 743 /* 744 * This code presumes that OSF1 will only 745 * give out quota info when the real uid 746 * matches the effective uid. JRA. 747 */ 748 euser_id = geteuid(); 749 save_re_uid(); 750 if (set_re_uid() != 0) return False; 751 752 r= quotactl(path,QCMD(Q_GETQUOTA, USRQUOTA),euser_id,(char *) &D); 753 if (r) { 754 save_errno = errno; 755 } 756 757 restore_re_uid(); 758 759 *bsize = DEV_BSIZE; 760 761 if (r) 762 { 763 if (save_errno == EDQUOT) /* disk quota exceeded */ 764 { 765 *dfree = 0; 766 *dsize = D.dqb_curblocks; 767 return (True); 768 } 769 else 770 return (False); 771 } 772 773 /* If softlimit is zero, set it equal to hardlimit. 774 */ 775 776 if (D.dqb_bsoftlimit==0) 777 D.dqb_bsoftlimit = D.dqb_bhardlimit; 778 779 /* Use softlimit to determine disk space, except when it has been exceeded */ 780 781 if (D.dqb_bsoftlimit==0) 782 return(False); 783 784 if ((D.dqb_curblocks>D.dqb_bsoftlimit)) { 785 *dfree = 0; 786 *dsize = D.dqb_curblocks; 787 } else { 788 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; 789 *dsize = D.dqb_bsoftlimit; 790 } 791 return (True); 792} 793 794#elif defined (IRIX6) 795/**************************************************************************** 796try to get the disk space from disk quotas (IRIX 6.2 version) 797****************************************************************************/ 798 799#include <sys/quota.h> 800#include <mntent.h> 801 802BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 803{ 804 uid_t euser_id; 805 int r; 806 struct dqblk D; 807 struct fs_disk_quota F; 808 SMB_STRUCT_STAT S; 809 FILE *fp; 810 struct mntent *mnt; 811 SMB_DEV_T devno; 812 int found; 813 814 /* find the block device file */ 815 816 if ( sys_stat(path, &S) == -1 ) { 817 return(False) ; 818 } 819 820 devno = S.st_dev ; 821 822 fp = setmntent(MOUNTED,"r"); 823 found = False ; 824 825 while ((mnt = getmntent(fp))) { 826 if ( sys_stat(mnt->mnt_dir,&S) == -1 ) 827 continue ; 828 if (S.st_dev == devno) { 829 found = True ; 830 break ; 831 } 832 } 833 endmntent(fp) ; 834 835 if (!found) { 836 return(False); 837 } 838 839 euser_id=geteuid(); 840 save_re_uid(); 841 set_effective_uid(0); 842 843 /* Use softlimit to determine disk space, except when it has been exceeded */ 844 845 *bsize = 512; 846 847 if ( 0 == strcmp ( mnt->mnt_type, "efs" )) 848 { 849 r=quotactl (Q_GETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &D); 850 851 restore_re_uid(); 852 853 if (r==-1) 854 return(False); 855 856 /* Use softlimit to determine disk space, except when it has been exceeded */ 857 if ( 858 (D.dqb_bsoftlimit && D.dqb_curblocks>=D.dqb_bsoftlimit) || 859 (D.dqb_bhardlimit && D.dqb_curblocks>=D.dqb_bhardlimit) || 860 (D.dqb_fsoftlimit && D.dqb_curfiles>=D.dqb_fsoftlimit) || 861 (D.dqb_fhardlimit && D.dqb_curfiles>=D.dqb_fhardlimit) 862 ) 863 { 864 *dfree = 0; 865 *dsize = D.dqb_curblocks; 866 } 867 else if (D.dqb_bsoftlimit==0 && D.dqb_bhardlimit==0) 868 { 869 return(False); 870 } 871 else 872 { 873 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; 874 *dsize = D.dqb_bsoftlimit; 875 } 876 877 } 878 else if ( 0 == strcmp ( mnt->mnt_type, "xfs" )) 879 { 880 r=quotactl (Q_XGETQUOTA, mnt->mnt_fsname, euser_id, (caddr_t) &F); 881 882 restore_re_uid(); 883 884 if (r==-1) 885 return(False); 886 887 /* Use softlimit to determine disk space, except when it has been exceeded */ 888 if ( 889 (F.d_blk_softlimit && F.d_bcount>=F.d_blk_softlimit) || 890 (F.d_blk_hardlimit && F.d_bcount>=F.d_blk_hardlimit) || 891 (F.d_ino_softlimit && F.d_icount>=F.d_ino_softlimit) || 892 (F.d_ino_hardlimit && F.d_icount>=F.d_ino_hardlimit) 893 ) 894 { 895 *dfree = 0; 896 *dsize = F.d_bcount; 897 } 898 else if (F.d_blk_softlimit==0 && F.d_blk_hardlimit==0) 899 { 900 return(False); 901 } 902 else 903 { 904 *dfree = (F.d_blk_softlimit - F.d_bcount); 905 *dsize = F.d_blk_softlimit; 906 } 907 908 } 909 else 910 { 911 restore_re_uid(); 912 return(False); 913 } 914 915 return (True); 916 917} 918 919#else 920 921#if defined(__FreeBSD__) || defined(__OpenBSD__) 922#include <ufs/ufs/quota.h> 923#include <machine/param.h> 924#elif AIX 925/* AIX quota patch from Ole Holm Nielsen <ohnielse@fysik.dtu.dk> */ 926#include <jfs/quota.h> 927/* AIX 4.X: Rename members of the dqblk structure (ohnielse@fysik.dtu.dk) */ 928#define dqb_curfiles dqb_curinodes 929#define dqb_fhardlimit dqb_ihardlimit 930#define dqb_fsoftlimit dqb_isoftlimit 931#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ 932#include <sys/quota.h> 933#include <devnm.h> 934#endif 935 936/**************************************************************************** 937try to get the disk space from disk quotas - default version 938****************************************************************************/ 939 940BOOL disk_quotas(const char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 941{ 942 int r; 943 struct dqblk D; 944 uid_t euser_id; 945#if !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) 946 char dev_disk[256]; 947 SMB_STRUCT_STAT S; 948 949 /* find the block device file */ 950 951#ifdef HPUX 952 /* Need to set the cache flag to 1 for HPUX. Seems 953 * to have a significant performance boost when 954 * lstat calls on /dev access this function. 955 */ 956 if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 1)<0)) 957#else 958 if ((sys_stat(path, &S)<0) || (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) 959 return (False); 960#endif /* ifdef HPUX */ 961 962#endif /* !defined(__FreeBSD__) && !defined(AIX) && !defined(__OpenBSD__) */ 963 964 euser_id = geteuid(); 965 966#ifdef HPUX 967 /* for HPUX, real uid must be same as euid to execute quotactl for euid */ 968 save_re_uid(); 969 if (set_re_uid() != 0) return False; 970 971 r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); 972 973 restore_re_uid(); 974#else 975#if defined(__FreeBSD__) || defined(__OpenBSD__) 976 { 977 /* FreeBSD patches from Marty Moll <martym@arbor.edu> */ 978 gid_t egrp_id; 979 980 save_re_uid(); 981 set_effective_uid(0); 982 983 egrp_id = getegid(); 984 r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D); 985 986 /* As FreeBSD has group quotas, if getting the user 987 quota fails, try getting the group instead. */ 988 if (r) { 989 r= quotactl(path,QCMD(Q_GETQUOTA,GRPQUOTA),egrp_id,(char *) &D); 990 } 991 992 restore_re_uid(); 993 } 994#elif defined(AIX) 995 /* AIX has both USER and GROUP quotas: 996 Get the USER quota (ohnielse@fysik.dtu.dk) */ 997 r= quotactl(path,QCMD(Q_GETQUOTA,USRQUOTA),euser_id,(char *) &D); 998#else /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ 999 r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); 1000#endif /* !__FreeBSD__ && !AIX && !__OpenBSD__ */ 1001#endif /* HPUX */ 1002 1003 /* Use softlimit to determine disk space, except when it has been exceeded */ 1004#if defined(__FreeBSD__) || defined(__OpenBSD__) 1005 *bsize = DEV_BSIZE; 1006#else /* !__FreeBSD__ && !__OpenBSD__ */ 1007 *bsize = 1024; 1008#endif /*!__FreeBSD__ && !__OpenBSD__ */ 1009 1010 if (r) 1011 { 1012 if (errno == EDQUOT) 1013 { 1014 *dfree =0; 1015 *dsize =D.dqb_curblocks; 1016 return (True); 1017 } 1018 else return(False); 1019 } 1020 1021 /* If softlimit is zero, set it equal to hardlimit. 1022 */ 1023 1024 if (D.dqb_bsoftlimit==0) 1025 D.dqb_bsoftlimit = D.dqb_bhardlimit; 1026 1027 if (D.dqb_bsoftlimit==0) 1028 return(False); 1029 /* Use softlimit to determine disk space, except when it has been exceeded */ 1030 if ((D.dqb_curblocks>D.dqb_bsoftlimit) 1031#if !defined(__FreeBSD__) && !defined(__OpenBSD__) 1032||((D.dqb_curfiles>D.dqb_fsoftlimit) && (D.dqb_fsoftlimit != 0)) 1033#endif 1034 ) { 1035 *dfree = 0; 1036 *dsize = D.dqb_curblocks; 1037 } 1038 else { 1039 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; 1040 *dsize = D.dqb_bsoftlimit; 1041 } 1042 return (True); 1043} 1044 1045#endif 1046 1047#if defined(VXFS_QUOTA) 1048 1049/**************************************************************************** 1050Try to get the disk space from Veritas disk quotas. 1051 David Lee <T.D.Lee@durham.ac.uk> August 1999. 1052 1053Background assumptions: 1054 Potentially under many Operating Systems. Initially Solaris 2. 1055 1056 My guess is that Veritas is largely, though not entirely, 1057 independent of OS. So I have separated it out. 1058 1059 There may be some details. For example, OS-specific "include" files. 1060 1061 It is understood that HPUX 10 somehow gets Veritas quotas without 1062 any special effort; if so, this routine need not be compiled in. 1063 Dirk De Wachter <Dirk.DeWachter@rug.ac.be> 1064 1065Warning: 1066 It is understood that Veritas do not publicly support this ioctl interface. 1067 Rather their preference would be for the user (us) to call the native 1068 OS and then for the OS itself to call through to the VxFS filesystem. 1069 Presumably HPUX 10, see above, does this. 1070 1071Hints for porting: 1072 Add your OS to "IFLIST" below. 1073 Get it to compile successfully: 1074 Almost certainly "include"s require attention: see SUNOS5. 1075 In the main code above, arrange for it to be called: see SUNOS5. 1076 Test! 1077 1078****************************************************************************/ 1079 1080/* "IFLIST" 1081 * This "if" is a list of ports: 1082 * if defined(OS1) || defined(OS2) || ... 1083 */ 1084#if defined(SUNOS5) 1085 1086#if defined(SUNOS5) 1087#include <sys/fs/vx_solaris.h> 1088#endif 1089#include <sys/fs/vx_machdep.h> 1090#include <sys/fs/vx_layout.h> 1091#include <sys/fs/vx_quota.h> 1092#include <sys/fs/vx_aioctl.h> 1093#include <sys/fs/vx_ioctl.h> 1094 1095BOOL disk_quotas_vxfs(const pstring name, char *path, SMB_BIG_UINT *bsize, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) 1096{ 1097 uid_t user_id, euser_id; 1098 int ret; 1099 struct vx_dqblk D; 1100 struct vx_quotctl quotabuf; 1101 struct vx_genioctl genbuf; 1102 pstring qfname; 1103 int file; 1104 1105 /* 1106 * "name" may or may not include a trailing "/quotas". 1107 * Arranging consistency of calling here in "quotas.c" may not be easy and 1108 * it might be easier to examine and adjust it here. 1109 * Fortunately, VxFS seems not to mind at present. 1110 */ 1111 pstrcpy(qfname, name) ; 1112 /* pstrcat(qfname, "/quotas") ; */ /* possibly examine and adjust "name" */ 1113 1114 euser_id = geteuid(); 1115 set_effective_uid(0); 1116 1117 DEBUG(5,("disk_quotas: looking for VxFS quotas file \"%s\"\n", qfname)); 1118 if((file=sys_open(qfname, O_RDONLY,0))<0) { 1119 set_effective_uid(euser_id); 1120 return(False); 1121 } 1122 genbuf.ioc_cmd = VX_QUOTACTL; 1123 genbuf.ioc_up = (void *) "abuf; 1124 1125 quotabuf.cmd = VX_GETQUOTA; 1126 quotabuf.uid = euser_id; 1127 quotabuf.addr = (caddr_t) &D; 1128 ret = ioctl(file, VX_ADMIN_IOCTL, &genbuf); 1129 close(file); 1130 1131 set_effective_uid(euser_id); 1132 1133 if (ret < 0) { 1134 DEBUG(5,("disk_quotas ioctl (VxFS) failed. Error = %s\n", strerror(errno) )); 1135 return(False); 1136 } 1137 1138 /* If softlimit is zero, set it equal to hardlimit. 1139 */ 1140 1141 if (D.dqb_bsoftlimit==0) 1142 D.dqb_bsoftlimit = D.dqb_bhardlimit; 1143 1144 /* Use softlimit to determine disk space. A user exceeding the quota is told 1145 * that there's no space left. Writes might actually work for a bit if the 1146 * hardlimit is set higher than softlimit. Effectively the disk becomes 1147 * made of rubber latex and begins to expand to accommodate the user :-) 1148 */ 1149 DEBUG(5,("disk_quotas for path \"%s\" block c/s/h %ld/%ld/%ld; file c/s/h %ld/%ld/%ld\n", 1150 path, D.dqb_curblocks, D.dqb_bsoftlimit, D.dqb_bhardlimit, 1151 D.dqb_curfiles, D.dqb_fsoftlimit, D.dqb_fhardlimit)); 1152 1153 if (D.dqb_bsoftlimit==0) 1154 return(False); 1155 *bsize = DEV_BSIZE; 1156 *dsize = D.dqb_bsoftlimit; 1157 1158 if (D.dqb_curblocks > D.dqb_bsoftlimit) { 1159 *dfree = 0; 1160 *dsize = D.dqb_curblocks; 1161 } else 1162 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; 1163 1164 DEBUG(5,("disk_quotas for path \"%s\" returning bsize %llu, dfree %llu, dsize %llu\n", 1165 path,*bsize,*dfree,*dsize)); 1166 1167 return(True); 1168} 1169 1170#endif /* SUNOS5 || ... */ 1171 1172#endif /* VXFS_QUOTA */ 1173 1174#else /* WITH_QUOTAS */ 1175 1176BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) 1177{ 1178 (*bsize) = 512; /* This value should be ignored */ 1179 1180 /* And just to be sure we set some values that hopefully */ 1181 /* will be larger that any possible real-world value */ 1182 (*dfree) = (SMB_BIG_UINT)-1; 1183 (*dsize) = (SMB_BIG_UINT)-1; 1184 1185 /* As we have select not to use quotas, allways fail */ 1186 return False; 1187} 1188#endif /* WITH_QUOTAS */ 1189 1190#else /* HAVE_SYS_QUOTAS */ 1191/* wrapper to the new sys_quota interface 1192 this file should be removed later 1193 */ 1194BOOL disk_quotas(const char *path,SMB_BIG_UINT *bsize,SMB_BIG_UINT *dfree,SMB_BIG_UINT *dsize) 1195{ 1196 int r; 1197 SMB_DISK_QUOTA D; 1198 unid_t id; 1199 1200 id.uid = geteuid(); 1201 1202 ZERO_STRUCT(D); 1203 r=sys_get_quota(path, SMB_USER_QUOTA_TYPE, id, &D); 1204 1205 /* Use softlimit to determine disk space, except when it has been exceeded */ 1206 *bsize = D.bsize; 1207 if (r == -1) { 1208 if (errno == EDQUOT) { 1209 *dfree =0; 1210 *dsize =D.curblocks; 1211 return (True); 1212 } else { 1213 goto try_group_quota; 1214 } 1215 } 1216 1217 /* Use softlimit to determine disk space, except when it has been exceeded */ 1218 if ( 1219 (D.softlimit && D.curblocks >= D.softlimit) || 1220 (D.hardlimit && D.curblocks >= D.hardlimit) || 1221 (D.isoftlimit && D.curinodes >= D.isoftlimit) || 1222 (D.ihardlimit && D.curinodes>=D.ihardlimit) 1223 ) { 1224 *dfree = 0; 1225 *dsize = D.curblocks; 1226 } else if (D.softlimit==0 && D.hardlimit==0) { 1227 goto try_group_quota; 1228 } else { 1229 if (D.softlimit == 0) 1230 D.softlimit = D.hardlimit; 1231 *dfree = D.softlimit - D.curblocks; 1232 *dsize = D.softlimit; 1233 } 1234 1235 return True; 1236 1237try_group_quota: 1238 id.gid = getegid(); 1239 1240 ZERO_STRUCT(D); 1241 r=sys_get_quota(path, SMB_GROUP_QUOTA_TYPE, id, &D); 1242 1243 /* Use softlimit to determine disk space, except when it has been exceeded */ 1244 *bsize = D.bsize; 1245 if (r == -1) { 1246 if (errno == EDQUOT) { 1247 *dfree =0; 1248 *dsize =D.curblocks; 1249 return (True); 1250 } else { 1251 return False; 1252 } 1253 } 1254 1255 /* Use softlimit to determine disk space, except when it has been exceeded */ 1256 if ( 1257 (D.softlimit && D.curblocks >= D.softlimit) || 1258 (D.hardlimit && D.curblocks >= D.hardlimit) || 1259 (D.isoftlimit && D.curinodes >= D.isoftlimit) || 1260 (D.ihardlimit && D.curinodes>=D.ihardlimit) 1261 ) { 1262 *dfree = 0; 1263 *dsize = D.curblocks; 1264 } else if (D.softlimit==0 && D.hardlimit==0) { 1265 return False; 1266 } else { 1267 if (D.softlimit == 0) 1268 D.softlimit = D.hardlimit; 1269 *dfree = D.softlimit - D.curblocks; 1270 *dsize = D.softlimit; 1271 } 1272 1273 return (True); 1274} 1275#endif /* HAVE_SYS_QUOTAS */ 1276