quota: add supprt for gfs2
[metze/samba/wip.git] / source3 / lib / sysquotas.c
1 /* 
2    Unix SMB/CIFS implementation.
3    System QUOTA function wrappers
4    Copyright (C) Stefan (metze) Metzmacher      2003
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 #include "includes.h"
22
23 #undef DBGC_CLASS
24 #define DBGC_CLASS DBGC_QUOTA
25
26 #ifdef HAVE_SYS_QUOTAS
27
28 #if defined(HAVE_QUOTACTL_4A) 
29
30 /*#endif HAVE_QUOTACTL_4A */
31 #elif defined(HAVE_QUOTACTL_4B)
32
33 #error HAVE_QUOTACTL_4B not implemeted
34
35 /*#endif HAVE_QUOTACTL_4B */
36 #elif defined(HAVE_QUOTACTL_3)
37
38 #error HAVE_QUOTACTL_3 not implemented
39
40 /* #endif  HAVE_QUOTACTL_3 */
41 #else /* NO_QUOTACTL_USED */
42
43 #endif /* NO_QUOTACTL_USED */
44
45 #ifdef HAVE_MNTENT
46 static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
47 {
48         int ret = -1;
49         SMB_STRUCT_STAT S;
50         FILE *fp;
51         struct mntent *mnt;
52         SMB_DEV_T devno;
53
54         /* find the block device file */
55
56         if (!path||!mntpath||!bdev||!fs)
57                 smb_panic("sys_path_to_bdev: called with NULL pointer");
58
59         (*mntpath) = NULL;
60         (*bdev) = NULL;
61         (*fs) = NULL;
62         
63         if ( sys_stat(path, &S, false) == -1 )
64                 return (-1);
65
66         devno = S.st_ex_dev ;
67
68         fp = setmntent(MOUNTED,"r");
69         if (fp == NULL) {
70                 return -1;
71         }
72   
73         while ((mnt = getmntent(fp))) {
74                 if ( sys_stat(mnt->mnt_dir, &S, false) == -1 )
75                         continue ;
76
77                 if (S.st_ex_dev == devno) {
78                         (*mntpath) = SMB_STRDUP(mnt->mnt_dir);
79                         (*bdev) = SMB_STRDUP(mnt->mnt_fsname);
80                         (*fs)   = SMB_STRDUP(mnt->mnt_type);
81                         if ((*mntpath)&&(*bdev)&&(*fs)) {
82                                 ret = 0;
83                         } else {
84                                 SAFE_FREE(*mntpath);
85                                 SAFE_FREE(*bdev);
86                                 SAFE_FREE(*fs);
87                                 ret = -1;
88                         }
89
90                         break;
91                 }
92         }
93
94         endmntent(fp) ;
95
96         return ret;
97 }
98 /* #endif HAVE_MNTENT */
99 #elif defined(HAVE_DEVNM)
100
101 /* we have this on HPUX, ... */
102 static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
103 {
104         int ret = -1;
105         char dev_disk[256];
106         SMB_STRUCT_STAT S;
107
108         if (!path||!mntpath||!bdev||!fs)
109                 smb_panic("sys_path_to_bdev: called with NULL pointer");
110
111         (*mntpath) = NULL;
112         (*bdev) = NULL;
113         (*fs) = NULL;
114         
115         /* find the block device file */
116
117         if ((ret=sys_stat(path, &S, false))!=0) {
118                 return ret;
119         }
120         
121         if ((ret=devnm(S_IFBLK, S.st_ex_dev, dev_disk, 256, 1))!=0) {
122                 return ret;     
123         }
124
125         /* we should get the mntpath right...
126          * but I don't know how
127          * --metze
128          */
129         (*mntpath) = SMB_STRDUP(path);
130         (*bdev) = SMB_STRDUP(dev_disk);
131         if ((*mntpath)&&(*bdev)) {
132                 ret = 0;
133         } else {
134                 SAFE_FREE(*mntpath);
135                 SAFE_FREE(*bdev);
136                 ret = -1;
137         }       
138         
139         
140         return ret;     
141 }
142
143 /* #endif HAVE_DEVNM */
144 #else
145 /* we should fake this up...*/
146 static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
147 {
148         int ret = -1;
149
150         if (!path||!mntpath||!bdev||!fs)
151                 smb_panic("sys_path_to_bdev: called with NULL pointer");
152
153         (*mntpath) = NULL;
154         (*bdev) = NULL;
155         (*fs) = NULL;
156         
157         (*mntpath) = SMB_STRDUP(path);
158         if (*mntpath) {
159                 ret = 0;
160         } else {
161                 SAFE_FREE(*mntpath);
162                 ret = -1;
163         }
164
165         return ret;
166 }
167 #endif
168
169 /*********************************************************************
170  Now the list of all filesystem specific quota systems we have found
171 **********************************************************************/
172 static struct {
173         const char *name;
174         int (*get_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
175         int (*set_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
176 } sys_quota_backends[] = {
177 #ifdef HAVE_XFS_QUOTAS
178         {"xfs", sys_get_xfs_quota,      sys_set_xfs_quota},
179         {"gfs", sys_get_xfs_quota,      sys_set_xfs_quota},
180         {"gfs2", sys_get_xfs_quota,     sys_set_xfs_quota},
181 #endif /* HAVE_XFS_QUOTAS */
182 #ifdef HAVE_NFS_QUOTAS
183         {"nfs", sys_get_nfs_quota,      sys_set_nfs_quota},
184         {"nfs4", sys_get_nfs_quota,     sys_set_nfs_quota},
185 #endif /* HAVE_NFS_QUOTAS */
186         {NULL,  NULL,                   NULL}
187 };
188
189 static int command_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
190 {
191         const char *get_quota_command;
192         char **lines = NULL;
193
194         get_quota_command = lp_get_quota_command();
195         if (get_quota_command && *get_quota_command) {
196                 const char *p;
197                 char *p2;
198                 char *syscmd = NULL;
199                 int _id = -1;
200
201                 switch(qtype) {
202                         case SMB_USER_QUOTA_TYPE:
203                         case SMB_USER_FS_QUOTA_TYPE:
204                                 _id = id.uid;
205                                 break;
206                         case SMB_GROUP_QUOTA_TYPE:
207                         case SMB_GROUP_FS_QUOTA_TYPE:
208                                 _id = id.gid;
209                                 break;
210                         default:
211                                 DEBUG(0,("invalid quota type.\n"));
212                                 return -1;
213                 }
214
215                 if (asprintf(&syscmd, "%s \"%s\" %d %d",
216                         get_quota_command, path, qtype, _id) < 0) {
217                         return -1;
218                 }
219
220                 DEBUG (3, ("get_quota: Running command %s\n", syscmd));
221
222                 lines = file_lines_pload(syscmd, NULL);
223                 SAFE_FREE(syscmd);
224
225                 if (lines) {
226                         char *line = lines[0];
227
228                         DEBUG (3, ("Read output from get_quota, \"%s\"\n", line));
229
230                         /* we need to deal with long long unsigned here, if supported */
231
232                         dp->qflags = (enum SMB_QUOTA_TYPE)strtoul(line, &p2, 10);
233                         p = p2;
234                         while (p && *p && isspace(*p)) {
235                                 p++;
236                         }
237
238                         if (p && *p) {
239                                 dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p);
240                         } else {
241                                 goto invalid_param;
242                         }
243
244                         while (p && *p && isspace(*p)) {
245                                 p++;
246                         }
247
248                         if (p && *p) {
249                                 dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p);
250                         } else {
251                                 goto invalid_param;
252                         }
253
254                         while (p && *p && isspace(*p)) {
255                                 p++;
256                         }
257
258                         if (p && *p) {
259                                 dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p);
260                         } else {
261                                 goto invalid_param;
262                         }
263
264                         while (p && *p && isspace(*p)) {
265                                 p++;
266                         }
267
268                         if (p && *p) {
269                                 dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p);
270                         } else {
271                                 goto invalid_param;
272                         }
273
274                         while (p && *p && isspace(*p)) {
275                                 p++;
276                         }
277
278                         if (p && *p) {
279                                 dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p);
280                         } else {
281                                 goto invalid_param;
282                         }
283
284                         while (p && *p && isspace(*p)) {
285                                 p++;
286                         }
287
288                         if (p && *p) {
289                                 dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p);
290                         } else {
291                                 goto invalid_param;     
292                         }
293
294                         while (p && *p && isspace(*p)) {
295                                 p++;
296                         }
297
298                         if (p && *p) {
299                                 dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL);
300                         } else {
301                                 dp->bsize = 1024;
302                         }
303
304                         TALLOC_FREE(lines);
305                         lines = NULL;
306
307                         DEBUG (3, ("Parsed output of get_quota, ...\n"));
308
309                         DEBUGADD (5,( 
310                                 "qflags:%u curblocks:%llu softlimit:%llu hardlimit:%llu\n"
311                                 "curinodes:%llu isoftlimit:%llu ihardlimit:%llu bsize:%llu\n", 
312                                 dp->qflags,(long long unsigned)dp->curblocks,
313                                 (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
314                                 (long long unsigned)dp->curinodes,
315                                 (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
316                                 (long long unsigned)dp->bsize));
317                         return 0;
318                 }
319
320                 DEBUG (0, ("get_quota_command failed!\n"));
321                 return -1;
322         }
323
324         errno = ENOSYS;
325         return -1;
326
327 invalid_param:
328
329         TALLOC_FREE(lines);
330         DEBUG(0,("The output of get_quota_command is invalid!\n"));
331         return -1;
332 }
333
334 static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
335 {
336         const char *set_quota_command;
337
338         set_quota_command = lp_set_quota_command();
339         if (set_quota_command && *set_quota_command) {
340                 char **lines = NULL;
341                 char *syscmd = NULL;
342                 int _id = -1;
343
344                 switch(qtype) {
345                         case SMB_USER_QUOTA_TYPE:
346                         case SMB_USER_FS_QUOTA_TYPE:
347                                 _id = id.uid;
348                                 break;
349                         case SMB_GROUP_QUOTA_TYPE:
350                         case SMB_GROUP_FS_QUOTA_TYPE:
351                                 _id = id.gid;
352                                 break;
353                         default:
354                                 return -1;
355                 }
356
357                 if (asprintf(&syscmd,
358                         "%s \"%s\" %d %d "
359                         "%u %llu %llu "
360                         "%llu %llu %llu ",
361                         set_quota_command, path, qtype, _id, dp->qflags,
362                         (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
363                         (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
364                         (long long unsigned)dp->bsize) < 0) {
365                         return -1;
366                 }
367
368                 DEBUG (3, ("get_quota: Running command %s\n", syscmd));
369
370                 lines = file_lines_pload(syscmd, NULL);
371                 SAFE_FREE(syscmd);
372                 if (lines) {
373                         char *line = lines[0];
374
375                         DEBUG (3, ("Read output from set_quota, \"%s\"\n", line));
376
377                         TALLOC_FREE(lines);
378
379                         return 0;
380                 }
381                 DEBUG (0, ("set_quota_command failed!\n"));
382                 return -1;
383         }
384
385         errno = ENOSYS;
386         return -1;
387 }
388
389 int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
390 {
391         int ret = -1;
392         int i;
393         bool ready = False;
394         char *mntpath = NULL;
395         char *bdev = NULL;
396         char *fs = NULL;
397
398         if (!path||!dp)
399                 smb_panic("sys_get_quota: called with NULL pointer");
400
401         if (command_get_quota(path, qtype, id, dp)==0) {        
402                 return 0;
403         } else if (errno != ENOSYS) {
404                 return -1;
405         }
406
407         if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
408                 DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
409                 return ret;
410         }
411
412         errno = 0;
413         DEBUG(10,("sys_get_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
414
415         for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) {
416                 if (strcmp(fs,sys_quota_backends[i].name)==0) {
417                         ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp);
418                         if (ret!=0) {
419                                 DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
420                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
421                         } else {
422                                 DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
423                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
424                         }
425                         ready = True;
426                         break;  
427                 }               
428         }
429
430         if (!ready) {
431                 /* use the default vfs quota functions */
432                 ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp);
433                 if (ret!=0) {
434                         DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n",
435                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
436                 } else {
437                         DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
438                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
439                 }
440         }
441
442         SAFE_FREE(mntpath);
443         SAFE_FREE(bdev);
444         SAFE_FREE(fs);
445
446         if ((ret!=0)&& (errno == EDQUOT)) {
447                 DEBUG(10,("sys_get_quota() warning over quota!\n"));
448                 return 0;
449         }
450
451         return ret;
452 }
453
454 int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
455 {
456         int ret = -1;
457         int i;
458         bool ready = False;
459         char *mntpath = NULL;
460         char *bdev = NULL;
461         char *fs = NULL;
462
463         /* find the block device file */
464
465         if (!path||!dp)
466                 smb_panic("get_smb_quota: called with NULL pointer");
467
468         if (command_set_quota(path, qtype, id, dp)==0) {        
469                 return 0;
470         } else if (errno != ENOSYS) {
471                 return -1;
472         }
473
474         if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
475                 DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
476                 return ret;
477         }
478
479         errno = 0;
480         DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid())); 
481
482         for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) {
483                 if (strcmp(fs,sys_quota_backends[i].name)==0) {
484                         ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp);
485                         if (ret!=0) {
486                                 DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
487                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
488                         } else {
489                                 DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
490                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
491                         }
492                         ready = True;
493                         break;
494                 }               
495         }
496
497         if (!ready) {
498                 /* use the default vfs quota functions */
499                 ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp);
500                 if (ret!=0) {
501                         DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
502                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
503                 } else {
504                         DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
505                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
506                 }
507         }
508
509         SAFE_FREE(mntpath);
510         SAFE_FREE(bdev);
511         SAFE_FREE(fs);
512
513         if ((ret!=0)&& (errno == EDQUOT)) {
514                 DEBUG(10,("sys_set_quota() warning over quota!\n"));
515                 return 0;
516         }
517
518         return ret;             
519 }
520
521 #else /* HAVE_SYS_QUOTAS */
522  void dummy_sysquotas_c(void);
523
524  void dummy_sysquotas_c(void)
525 {
526         return;
527 }
528 #endif /* HAVE_SYS_QUOTAS */
529