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, "astat)) {
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 1635 : 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 1635 : ZERO_STRUCT(D);
374 1635 : id.uid = -1;
375 1635 : r = SMB_VFS_GET_QUOTA(conn, fname, SMB_USER_FS_QUOTA_TYPE,
376 : id, &D);
377 1635 : if (r == -1 && errno != ENOSYS) {
378 0 : goto try_group_quota;
379 : }
380 1635 : if (r == 0 && (D.qflags & QUOTAS_DENY_DISK) == 0) {
381 1589 : goto try_group_quota;
382 : }
383 :
384 46 : ZERO_STRUCT(D);
385 46 : 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 46 : if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO &&
392 4 : id.uid != fname->st.st_ex_uid && id.uid != sec_initial_uid()) {
393 0 : int save_errno;
394 :
395 2 : id.uid = fname->st.st_ex_uid;
396 2 : become_root();
397 2 : r = SMB_VFS_GET_QUOTA(conn, fname,
398 : SMB_USER_QUOTA_TYPE, id, &D);
399 2 : save_errno = errno;
400 2 : unbecome_root();
401 2 : errno = save_errno;
402 : } else {
403 44 : r = SMB_VFS_GET_QUOTA(conn, fname,
404 : SMB_USER_QUOTA_TYPE, id, &D);
405 : }
406 :
407 46 : if (r == -1) {
408 2 : goto try_group_quota;
409 : }
410 :
411 44 : *bsize = D.bsize;
412 : /* Use softlimit to determine disk space, except when it has been exceeded */
413 44 : if (
414 44 : (D.softlimit && D.curblocks >= D.softlimit) ||
415 42 : (D.hardlimit && D.curblocks >= D.hardlimit) ||
416 40 : (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
417 38 : (D.ihardlimit && D.curinodes>=D.ihardlimit)
418 : ) {
419 8 : *dfree = 0;
420 8 : *dsize = D.curblocks;
421 36 : } else if (D.softlimit==0 && D.hardlimit==0) {
422 14 : goto try_group_quota;
423 : } else {
424 22 : if (D.softlimit == 0) {
425 2 : D.softlimit = D.hardlimit;
426 : }
427 22 : *dfree = D.softlimit - D.curblocks;
428 22 : *dsize = D.softlimit;
429 : }
430 :
431 30 : return True;
432 :
433 1605 : 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 1605 : ZERO_STRUCT(D);
440 1605 : id.gid = -1;
441 1605 : r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_FS_QUOTA_TYPE,
442 : id, &D);
443 1605 : if (r == -1 && errno != ENOSYS) {
444 0 : return false;
445 : }
446 1605 : if (r == 0 && (D.qflags & QUOTAS_DENY_DISK) == 0) {
447 1587 : return false;
448 : }
449 :
450 18 : 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 18 : if (VALID_STAT(fname->st) &&
458 18 : S_ISDIR(fname->st.st_ex_mode) &&
459 18 : fname->st.st_ex_mode & S_ISGID) {
460 2 : id.gid = fname->st.st_ex_gid;
461 2 : become_root();
462 2 : r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_QUOTA_TYPE, id,
463 : &D);
464 2 : unbecome_root();
465 : } else {
466 16 : id.gid = getegid();
467 16 : r = SMB_VFS_GET_QUOTA(conn, fname, SMB_GROUP_QUOTA_TYPE, id,
468 : &D);
469 : }
470 :
471 18 : if (r == -1) {
472 0 : return False;
473 : }
474 :
475 18 : *bsize = D.bsize;
476 : /* Use softlimit to determine disk space, except when it has been exceeded */
477 18 : if (
478 18 : (D.softlimit && D.curblocks >= D.softlimit) ||
479 18 : (D.hardlimit && D.curblocks >= D.hardlimit) ||
480 18 : (D.isoftlimit && D.curinodes >= D.isoftlimit) ||
481 18 : (D.ihardlimit && D.curinodes>=D.ihardlimit)
482 : ) {
483 0 : *dfree = 0;
484 0 : *dsize = D.curblocks;
485 18 : } else if (D.softlimit==0 && D.hardlimit==0) {
486 12 : return False;
487 : } else {
488 6 : if (D.softlimit == 0) {
489 0 : D.softlimit = D.hardlimit;
490 : }
491 6 : *dfree = D.softlimit - D.curblocks;
492 6 : *dsize = D.softlimit;
493 : }
494 :
495 6 : return (True);
496 : }
497 : #endif /* HAVE_SYS_QUOTAS */
|