LCOV - code coverage report
Current view: top level - source3/smbd - quotas.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 65 75 86.7 %
Date: 2024-02-28 12:06:22 Functions: 1 1 100.0 %

          Line data    Source code
       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 3 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, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : 
      21             : /*
      22             :  * This is one of the most system dependent parts of Samba, and its
      23             :  * done a little differently. Each system has its own way of doing
      24             :  * things :-(
      25             :  */
      26             : 
      27             : #include "includes.h"
      28             : #include "smbd/smbd.h"
      29             : #include "system/filesys.h"
      30             : 
      31             : #undef DBGC_CLASS
      32             : #define DBGC_CLASS DBGC_QUOTA
      33             : 
      34             : #ifndef HAVE_SYS_QUOTAS
      35             : 
      36             : /* just a quick hack because sysquotas.h is included before linux/quota.h */
      37             : #ifdef QUOTABLOCK_SIZE
      38             : #undef QUOTABLOCK_SIZE
      39             : #endif
      40             : 
      41             : #ifdef WITH_QUOTAS
      42             : 
      43             : #if defined(SUNOS5) /* Solaris */
      44             : 
      45             : #include <fcntl.h>
      46             : #include <sys/param.h>
      47             : #include <sys/fs/ufs_quota.h>
      48             : #include <sys/mnttab.h>
      49             : #include <sys/mntent.h>
      50             : 
      51             : /****************************************************************************
      52             :  Allows querying of remote hosts for quotas on NFS mounted shares.
      53             :  Supports normal NFS and AMD mounts.
      54             :  Alan Romeril <a.romeril@ic.ac.uk> July 2K.
      55             : ****************************************************************************/
      56             : 
      57             : #include <rpc/rpc.h>
      58             : #include <rpc/types.h>
      59             : #include <rpcsvc/rquota.h>
      60             : #include <rpc/nettype.h>
      61             : #include <rpc/xdr.h>
      62             : 
      63             : static int my_xdr_getquota_rslt(XDR *xdrsp, struct getquota_rslt *gqr)
      64             : {
      65             :         int quotastat;
      66             : 
      67             :         if (!xdr_int(xdrsp, &quotastat)) {
      68             :                 DEBUG(6,("nfs_quotas: Status bad or zero\n"));
      69             :                 return 0;
      70             :         }
      71             :         gqr->status = quotastat;
      72             : 
      73             :         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsize)) {
      74             :                 DEBUG(6,("nfs_quotas: Block size bad or zero\n"));
      75             :                 return 0;
      76             :         }
      77             :         if (!xdr_bool(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_active)) {
      78             :                 DEBUG(6,("nfs_quotas: Active bad or zero\n"));
      79             :                 return 0;
      80             :         }
      81             :         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bhardlimit)) {
      82             :                 DEBUG(6,("nfs_quotas: Hardlimit bad or zero\n"));
      83             :                 return 0;
      84             :         }
      85             :         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_bsoftlimit)) {
      86             :                 DEBUG(6,("nfs_quotas: Softlimit bad or zero\n"));
      87             :                 return 0;
      88             :         }
      89             :         if (!xdr_int(xdrsp, &gqr->getquota_rslt_u.gqr_rquota.rq_curblocks)) {
      90             :                 DEBUG(6,("nfs_quotas: Currentblocks bad or zero\n"));
      91             :                 return 0;
      92             :         }
      93             :         return (1);
      94             : }
      95             : 
      96             : static int my_xdr_getquota_args(XDR *xdrsp, struct getquota_args *args)
      97             : {
      98             :         if (!xdr_string(xdrsp, &args->gqa_pathp, RQ_PATHLEN ))
      99             :                 return(0);
     100             :         if (!xdr_int(xdrsp, &args->gqa_uid))
     101             :                 return(0);
     102             :         return (1);
     103             : }
     104             : 
     105             : /* Restricted to SUNOS5 for the moment, I haven`t access to others to test. */
     106             : static bool nfs_quotas(char *nfspath, uid_t euser_id, uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
     107             : {
     108             :         uid_t uid = euser_id;
     109             :         struct dqblk D;
     110             :         char *mnttype = nfspath;
     111             :         CLIENT *clnt;
     112             :         struct getquota_rslt gqr;
     113             :         struct getquota_args args;
     114             :         char *cutstr, *pathname, *host, *testpath;
     115             :         int len;
     116             :         static struct timeval timeout = {2,0};
     117             :         enum clnt_stat clnt_stat;
     118             :         bool ret = True;
     119             : 
     120             :         *bsize = *dfree = *dsize = (uint64_t)0;
     121             : 
     122             :         len=strcspn(mnttype, ":");
     123             :         pathname=strstr(mnttype, ":");
     124             :         cutstr = (char *) SMB_MALLOC(len+1);
     125             :         if (!cutstr)
     126             :                 return False;
     127             : 
     128             :         memset(cutstr, '\0', len+1);
     129             :         host = strncat(cutstr,mnttype, sizeof(char) * len );
     130             :         DEBUG(5,("nfs_quotas: looking for mount on \"%s\"\n", cutstr));
     131             :         DEBUG(5,("nfs_quotas: of path \"%s\"\n", mnttype));
     132             :         testpath=strchr_m(mnttype, ':');
     133             :         args.gqa_pathp = testpath+1;
     134             :         args.gqa_uid = uid;
     135             : 
     136             :         DEBUG(5,("nfs_quotas: Asking for host \"%s\" rpcprog \"%i\" rpcvers \"%i\" network \"%s\"\n", host, RQUOTAPROG, RQUOTAVERS, "udp"));
     137             : 
     138             :         if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) == NULL) {
     139             :                 ret = False;
     140             :                 goto out;
     141             :         }
     142             : 
     143             :         clnt->cl_auth = authunix_create_default();
     144             :         DEBUG(9,("nfs_quotas: auth_success\n"));
     145             : 
     146             :         clnt_stat=clnt_call(clnt, RQUOTAPROC_GETQUOTA, my_xdr_getquota_args, (caddr_t)&args, my_xdr_getquota_rslt, (caddr_t)&gqr, timeout);
     147             : 
     148             :         if (clnt_stat != RPC_SUCCESS) {
     149             :                 DEBUG(9,("nfs_quotas: clnt_call fail\n"));
     150             :                 ret = False;
     151             :                 goto out;
     152             :         }
     153             : 
     154             :         /*
     155             :          * gqr.status returns 1 if quotas exist, 2 if there is
     156             :          * no quota set, and 3 if no permission to get the quota.
     157             :          * If 3, return something sensible.
     158             :          */
     159             : 
     160             :         switch (gqr.status) {
     161             :         case 1:
     162             :                 DEBUG(9,("nfs_quotas: Good quota data\n"));
     163             :                 D.dqb_bsoftlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit;
     164             :                 D.dqb_bhardlimit = gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit;
     165             :                 D.dqb_curblocks = gqr.getquota_rslt_u.gqr_rquota.rq_curblocks;
     166             :                 break;
     167             : 
     168             :         case 2:
     169             :         case 3:
     170             :                 D.dqb_bsoftlimit = 1;
     171             :                 D.dqb_curblocks = 1;
     172             :                 DEBUG(9,("nfs_quotas: Remote Quotas returned \"%i\" \n", gqr.status));
     173             :                 break;
     174             : 
     175             :         default:
     176             :                 DEBUG(9, ("nfs_quotas: Unknown Remote Quota Status \"%i\"\n",
     177             :                                 gqr.status));
     178             :                 ret = false;
     179             :                 goto out;
     180             :         }
     181             : 
     182             :         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",
     183             :                         gqr.status,
     184             :                         gqr.getquota_rslt_u.gqr_rquota.rq_bsize,
     185             :                         gqr.getquota_rslt_u.gqr_rquota.rq_active,
     186             :                         gqr.getquota_rslt_u.gqr_rquota.rq_bhardlimit,
     187             :                         gqr.getquota_rslt_u.gqr_rquota.rq_bsoftlimit,
     188             :                         gqr.getquota_rslt_u.gqr_rquota.rq_curblocks));
     189             : 
     190             :         *bsize = gqr.getquota_rslt_u.gqr_rquota.rq_bsize;
     191             :         *dsize = D.dqb_bsoftlimit;
     192             : 
     193             :         if (D.dqb_curblocks > D.dqb_bsoftlimit) {
     194             :                 *dfree = 0;
     195             :                 *dsize = D.dqb_curblocks;
     196             :         } else
     197             :                 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
     198             : 
     199             :   out:
     200             : 
     201             :         if (clnt) {
     202             :                 if (clnt->cl_auth)
     203             :                         auth_destroy(clnt->cl_auth);
     204             :                 clnt_destroy(clnt);
     205             :         }
     206             : 
     207             :         DEBUG(5,("nfs_quotas: For path \"%s\" returning  bsize %.0f, dfree %.0f, dsize %.0f\n",args.gqa_pathp,(double)*bsize,(double)*dfree,(double)*dsize));
     208             : 
     209             :         SAFE_FREE(cutstr);
     210             :         DEBUG(10,("nfs_quotas: End of nfs_quotas\n" ));
     211             :         return ret;
     212             : }
     213             : 
     214             : /****************************************************************************
     215             : try to get the disk space from disk quotas (SunOS & Solaris2 version)
     216             : Quota code by Peter Urbanec (amiga@cse.unsw.edu.au).
     217             : ****************************************************************************/
     218             : 
     219             : bool disk_quotas(connection_struct *conn, struct smb_filename *fname,
     220             :                  uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
     221             : {
     222             :         uid_t euser_id;
     223             :         int ret;
     224             :         struct dqblk D;
     225             :         struct quotctl command;
     226             :         int file;
     227             :         struct mnttab mnt;
     228             :         char *name = NULL;
     229             :         FILE *fd;
     230             :         SMB_STRUCT_STAT sbuf;
     231             :         SMB_DEV_T devno;
     232             :         bool found = false;
     233             :         const char *path = fname->base_name;
     234             : 
     235             :         euser_id = geteuid();
     236             : 
     237             :         devno = fname->st.st_ex_dev;
     238             :         DEBUG(5,("disk_quotas: looking for path \"%s\" devno=%x\n",
     239             :                 path, (unsigned int)devno));
     240             :         if ((fd = fopen(MNTTAB, "r")) == NULL) {
     241             :                 return false;
     242             :         }
     243             : 
     244             :         while (getmntent(fd, &mnt) == 0) {
     245             :                 if (sys_stat(mnt.mnt_mountp, &sbuf, false) == -1) {
     246             :                         continue;
     247             :                 }
     248             : 
     249             :                 DEBUG(5,("disk_quotas: testing \"%s\" devno=%x\n",
     250             :                         mnt.mnt_mountp, (unsigned int)devno));
     251             : 
     252             :                 /* quotas are only on vxfs, UFS or NFS */
     253             :                 if ((sbuf.st_ex_dev == devno) && (
     254             :                         strcmp( mnt.mnt_fstype, MNTTYPE_UFS ) == 0 ||
     255             :                         strcmp( mnt.mnt_fstype, "nfs" ) == 0    ||
     256             :                         strcmp( mnt.mnt_fstype, "vxfs" ) == 0 )) {
     257             :                                 found = true;
     258             :                                 name = talloc_asprintf(talloc_tos(),
     259             :                                                 "%s/quotas",
     260             :                                                 mnt.mnt_mountp);
     261             :                                 break;
     262             :                 }
     263             :         }
     264             : 
     265             :         fclose(fd);
     266             :         if (!found) {
     267             :                 return false;
     268             :         }
     269             : 
     270             :         if (!name) {
     271             :                 return false;
     272             :         }
     273             :         become_root();
     274             : 
     275             :         if (strcmp(mnt.mnt_fstype, "nfs") == 0) {
     276             :                 bool retval;
     277             :                 DEBUG(5,("disk_quotas: looking for mountpath (NFS) \"%s\"\n",
     278             :                                         mnt.mnt_special));
     279             :                 retval = nfs_quotas(mnt.mnt_special,
     280             :                                 euser_id, bsize, dfree, dsize);
     281             :                 unbecome_root();
     282             :                 return retval;
     283             :         }
     284             : 
     285             :         DEBUG(5,("disk_quotas: looking for quotas file \"%s\"\n", name));
     286             :         if((file=open(name, O_RDONLY,0))<0) {
     287             :                 unbecome_root();
     288             :                 return false;
     289             :         }
     290             :         command.op = Q_GETQUOTA;
     291             :         command.uid = euser_id;
     292             :         command.addr = (caddr_t) &D;
     293             :         ret = ioctl(file, Q_QUOTACTL, &command);
     294             :         close(file);
     295             : 
     296             :         unbecome_root();
     297             : 
     298             :         if (ret < 0) {
     299             :                 DEBUG(5,("disk_quotas ioctl (Solaris) failed. Error = %s\n",
     300             :                                         strerror(errno) ));
     301             : 
     302             :                 return false;
     303             :         }
     304             : 
     305             :         /* If softlimit is zero, set it equal to hardlimit.
     306             :          */
     307             : 
     308             :         if (D.dqb_bsoftlimit==0) {
     309             :                 D.dqb_bsoftlimit = D.dqb_bhardlimit;
     310             :         }
     311             : 
     312             :         /* Use softlimit to determine disk space. A user exceeding the quota
     313             :          * is told that there's no space left. Writes might actually work for
     314             :          * a bit if the hardlimit is set higher than softlimit. Effectively
     315             :          * the disk becomes made of rubber latex and begins to expand to
     316             :          * accommodate the user :-)
     317             :          */
     318             : 
     319             :         if (D.dqb_bsoftlimit==0)
     320             :                 return(False);
     321             :         *bsize = DEV_BSIZE;
     322             :         *dsize = D.dqb_bsoftlimit;
     323             : 
     324             :         if (D.dqb_curblocks > D.dqb_bsoftlimit) {
     325             :                 *dfree = 0;
     326             :                 *dsize = D.dqb_curblocks;
     327             :         } else {
     328             :                 *dfree = D.dqb_bsoftlimit - D.dqb_curblocks;
     329             :         }
     330             : 
     331             :         DEBUG(5,("disk_quotas for path \"%s\" returning "
     332             :                 "bsize %.0f, dfree %.0f, dsize %.0f\n",
     333             :                 path,(double)*bsize,(double)*dfree,(double)*dsize));
     334             : 
     335             :         return true;
     336             : }
     337             : 
     338             : #endif /* Solaris */
     339             : 
     340             : #else /* WITH_QUOTAS */
     341             : 
     342             : bool disk_quotas(connection_struct *conn, struct smb_filename *fname,
     343             :                  uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
     344             : {
     345             :         (*bsize) = 512; /* This value should be ignored */
     346             : 
     347             :         /* And just to be sure we set some values that hopefully */
     348             :         /* will be larger that any possible real-world value     */
     349             :         (*dfree) = (uint64_t)-1;
     350             :         (*dsize) = (uint64_t)-1;
     351             : 
     352             :         /* As we have select not to use quotas, always fail */
     353             :         return false;
     354             : }
     355             : #endif /* WITH_QUOTAS */
     356             : 
     357             : #else /* HAVE_SYS_QUOTAS */
     358             : /* wrapper to the new sys_quota interface
     359             :    this file should be removed later
     360             :    */
     361        1778 : bool disk_quotas(connection_struct *conn, struct smb_filename *fname,
     362             :                  uint64_t *bsize, uint64_t *dfree, uint64_t *dsize)
     363             : {
     364           0 :         int r;
     365           0 :         SMB_DISK_QUOTA D;
     366           0 :         unid_t id;
     367             : 
     368             :         /*
     369             :          * First of all, check whether user quota is
     370             :          * enforced. If the call fails, assume it is
     371             :          * not enforced.
     372             :          */
     373        1778 :         ZERO_STRUCT(D);
     374        1778 :         id.uid = -1;
     375        1778 :         r = SMB_VFS_GET_QUOTA(conn, fname, SMB_USER_FS_QUOTA_TYPE,
     376             :                               id, &D);
     377        1778 :         if (r == -1 && errno != ENOSYS) {
     378           0 :                 goto try_group_quota;
     379             :         }
     380        1778 :         if (r == 0 && (D.qflags & QUOTAS_DENY_DISK) == 0) {
     381        1709 :                 goto try_group_quota;
     382             :         }
     383             : 
     384          69 :         ZERO_STRUCT(D);
     385          69 :         id.uid = geteuid();
     386             : 
     387             :         /* if new files created under this folder get this
     388             :          * folder's UID, then available space is governed by
     389             :          * the quota of the folder's UID, not the creating user.
     390             :          */
     391          69 :         if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO &&
     392           6 :             id.uid != fname->st.st_ex_uid && id.uid != sec_initial_uid()) {
     393           0 :                 int save_errno;
     394             : 
     395           3 :                 id.uid = fname->st.st_ex_uid;
     396           3 :                 become_root();
     397           3 :                 r = SMB_VFS_GET_QUOTA(conn, fname,
     398             :                                       SMB_USER_QUOTA_TYPE, id, &D);
     399           3 :                 save_errno = errno;
     400           3 :                 unbecome_root();
     401           3 :                 errno = save_errno;
     402             :         } else {
     403          66 :                 r = SMB_VFS_GET_QUOTA(conn, fname,
     404             :                                       SMB_USER_QUOTA_TYPE, id, &D);
     405             :         }
     406             : 
     407          69 :         if (r == -1) {
     408           3 :                 goto try_group_quota;
     409             :         }
     410             : 
     411          66 :         *bsize = D.bsize;
     412             :         /* Use softlimit to determine disk space, except when it has been exceeded */
     413          66 :         if (
     414          66 :                 (D.softlimit && D.curblocks >= D.softlimit) ||
     415          63 :                 (D.hardlimit && D.curblocks >= D.hardlimit) ||
     416          60 :                 (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
     417          57 :                 (D.ihardlimit && D.curinodes>=D.ihardlimit)
     418             :         ) {
     419          12 :                 *dfree = 0;
     420          12 :                 *dsize = D.curblocks;
     421          54 :         } else if (D.softlimit==0 && D.hardlimit==0) {
     422          21 :                 goto try_group_quota;
     423             :         } else {
     424          33 :                 if (D.softlimit == 0) {
     425           3 :                         D.softlimit = D.hardlimit;
     426             :                 }
     427          33 :                 *dfree = D.softlimit - D.curblocks;
     428          33 :                 *dsize = D.softlimit;
     429             :         }
     430             : 
     431          45 :         return True;
     432             : 
     433        1733 : try_group_quota:
     434             :         /*
     435             :          * First of all, check whether group quota is
     436             :          * enforced. If the call fails, assume it is
     437             :          * not enforced.
     438             :          */
     439        1733 :         ZERO_STRUCT(D);
     440        1733 :         id.gid = -1;
     441        1733 :         r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_FS_QUOTA_TYPE,
     442             :                               id, &D);
     443        1733 :         if (r == -1 && errno != ENOSYS) {
     444           0 :                 return false;
     445             :         }
     446        1733 :         if (r == 0 && (D.qflags & QUOTAS_DENY_DISK) == 0) {
     447        1706 :                 return false;
     448             :         }
     449             : 
     450          27 :         ZERO_STRUCT(D);
     451             : 
     452             :         /*
     453             :          * If new files created under this folder get this folder's
     454             :          * GID, then available space is governed by the quota of the
     455             :          * folder's GID, not the primary group of the creating user.
     456             :          */
     457          27 :         if (VALID_STAT(fname->st) &&
     458          27 :             S_ISDIR(fname->st.st_ex_mode) &&
     459          27 :             fname->st.st_ex_mode & S_ISGID) {
     460           3 :                 id.gid = fname->st.st_ex_gid;
     461           3 :                 become_root();
     462           3 :                 r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_QUOTA_TYPE, id,
     463             :                                       &D);
     464           3 :                 unbecome_root();
     465             :         } else {
     466          24 :                 id.gid = getegid();
     467          24 :                 r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_QUOTA_TYPE, id,
     468             :                                       &D);
     469             :         }
     470             : 
     471          27 :         if (r == -1) {
     472           0 :                 return False;
     473             :         }
     474             : 
     475          27 :         *bsize = D.bsize;
     476             :         /* Use softlimit to determine disk space, except when it has been exceeded */
     477          27 :         if (
     478          27 :                 (D.softlimit && D.curblocks >= D.softlimit) ||
     479          27 :                 (D.hardlimit && D.curblocks >= D.hardlimit) ||
     480          27 :                 (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
     481          27 :                 (D.ihardlimit && D.curinodes>=D.ihardlimit)
     482             :         ) {
     483           0 :                 *dfree = 0;
     484           0 :                 *dsize = D.curblocks;
     485          27 :         } else if (D.softlimit==0 && D.hardlimit==0) {
     486          18 :                 return False;
     487             :         } else {
     488           9 :                 if (D.softlimit == 0) {
     489           0 :                         D.softlimit = D.hardlimit;
     490             :                 }
     491           9 :                 *dfree = D.softlimit - D.curblocks;
     492           9 :                 *dsize = D.softlimit;
     493             :         }
     494             : 
     495           9 :         return (True);
     496             : }
     497             : #endif /* HAVE_SYS_QUOTAS */

Generated by: LCOV version 1.14