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