Use common util_file code.
[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 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) == -1 )
64                 return (-1);
65
66         devno = S.st_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) == -1 )
75                         continue ;
76
77                 if (S.st_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))!=0) {
118                 return ret;
119         }
120         
121         if ((ret=devnm(S_IFBLK, S.st_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 #endif /* HAVE_XFS_QUOTAS */
180         {NULL,  NULL,                   NULL}
181 };
182
183 static int command_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
184 {
185         const char *get_quota_command;
186         char **lines = NULL;
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 *syscmd = NULL;
193                 int _id = -1;
194
195                 switch(qtype) {
196                         case SMB_USER_QUOTA_TYPE:
197                         case SMB_USER_FS_QUOTA_TYPE:
198                                 _id = id.uid;
199                                 break;
200                         case SMB_GROUP_QUOTA_TYPE:
201                         case SMB_GROUP_FS_QUOTA_TYPE:
202                                 _id = id.gid;
203                                 break;
204                         default:
205                                 DEBUG(0,("invalid quota type.\n"));
206                                 return -1;
207                 }
208
209                 if (asprintf(&syscmd, "%s \"%s\" %d %d",
210                         get_quota_command, path, qtype, _id) < 0) {
211                         return -1;
212                 }
213
214                 DEBUG (3, ("get_quota: Running command %s\n", syscmd));
215
216                 lines = file_lines_pload(syscmd, NULL);
217                 SAFE_FREE(syscmd);
218
219                 if (lines) {
220                         char *line = lines[0];
221
222                         DEBUG (3, ("Read output from get_quota, \"%s\"\n", line));
223
224                         /* we need to deal with long long unsigned here, if supported */
225
226                         dp->qflags = (enum SMB_QUOTA_TYPE)strtoul(line, &p2, 10);
227                         p = p2;
228                         while (p && *p && isspace(*p)) {
229                                 p++;
230                         }
231
232                         if (p && *p) {
233                                 dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p);
234                         } else {
235                                 goto invalid_param;
236                         }
237
238                         while (p && *p && isspace(*p)) {
239                                 p++;
240                         }
241
242                         if (p && *p) {
243                                 dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p);
244                         } else {
245                                 goto invalid_param;
246                         }
247
248                         while (p && *p && isspace(*p)) {
249                                 p++;
250                         }
251
252                         if (p && *p) {
253                                 dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p);
254                         } else {
255                                 goto invalid_param;
256                         }
257
258                         while (p && *p && isspace(*p)) {
259                                 p++;
260                         }
261
262                         if (p && *p) {
263                                 dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p);
264                         } else {
265                                 goto invalid_param;
266                         }
267
268                         while (p && *p && isspace(*p)) {
269                                 p++;
270                         }
271
272                         if (p && *p) {
273                                 dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p);
274                         } else {
275                                 goto invalid_param;
276                         }
277
278                         while (p && *p && isspace(*p)) {
279                                 p++;
280                         }
281
282                         if (p && *p) {
283                                 dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p);
284                         } else {
285                                 goto invalid_param;     
286                         }
287
288                         while (p && *p && isspace(*p)) {
289                                 p++;
290                         }
291
292                         if (p && *p) {
293                                 dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL);
294                         } else {
295                                 dp->bsize = 1024;
296                         }
297
298                         TALLOC_FREE(lines);
299                         lines = NULL;
300
301                         DEBUG (3, ("Parsed output of get_quota, ...\n"));
302
303 #ifdef LARGE_SMB_OFF_T
304                         DEBUGADD (5,( 
305                                 "qflags:%u curblocks:%llu softlimit:%llu hardlimit:%llu\n"
306                                 "curinodes:%llu isoftlimit:%llu ihardlimit:%llu bsize:%llu\n", 
307                                 dp->qflags,(long long unsigned)dp->curblocks,
308                                 (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
309                                 (long long unsigned)dp->curinodes,
310                                 (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
311                                 (long long unsigned)dp->bsize));
312 #else /* LARGE_SMB_OFF_T */
313                         DEBUGADD (5,( 
314                                 "qflags:%u curblocks:%lu softlimit:%lu hardlimit:%lu\n"
315                                 "curinodes:%lu isoftlimit:%lu ihardlimit:%lu bsize:%lu\n", 
316                                 dp->qflags,(long unsigned)dp->curblocks,
317                                 (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit,
318                                 (long unsigned)dp->curinodes,
319                                 (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit,
320                                 (long unsigned)dp->bsize));
321 #endif /* LARGE_SMB_OFF_T */
322                         return 0;
323                 }
324
325                 DEBUG (0, ("get_quota_command failed!\n"));
326                 return -1;
327         }
328
329         errno = ENOSYS;
330         return -1;
331
332 invalid_param:
333
334         TALLOC_FREE(lines);
335         DEBUG(0,("The output of get_quota_command is invalid!\n"));
336         return -1;
337 }
338
339 static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
340 {
341         const char *set_quota_command;
342
343         set_quota_command = lp_set_quota_command();
344         if (set_quota_command && *set_quota_command) {
345                 char **lines = NULL;
346                 char *syscmd = NULL;
347                 int _id = -1;
348
349                 switch(qtype) {
350                         case SMB_USER_QUOTA_TYPE:
351                         case SMB_USER_FS_QUOTA_TYPE:
352                                 _id = id.uid;
353                                 break;
354                         case SMB_GROUP_QUOTA_TYPE:
355                         case SMB_GROUP_FS_QUOTA_TYPE:
356                                 _id = id.gid;
357                                 break;
358                         default:
359                                 return -1;
360                 }
361
362 #ifdef LARGE_SMB_OFF_T
363                 if (asprintf(&syscmd,
364                         "%s \"%s\" %d %d "
365                         "%u %llu %llu "
366                         "%llu %llu %llu ",
367                         set_quota_command, path, qtype, _id, dp->qflags,
368                         (long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
369                         (long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
370                         (long long unsigned)dp->bsize) < 0) {
371                         return -1;
372                 }
373 #else /* LARGE_SMB_OFF_T */
374                 if (asprintf(&syscmd,
375                         "%s \"%s\" %d %d "
376                         "%u %lu %lu "
377                         "%lu %lu %lu ",
378                         set_quota_command, path, qtype, _id, dp->qflags,
379                         (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit,
380                         (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit,
381                         (long unsigned)dp->bsize) < 0) {
382                         return -1;
383                 }
384 #endif /* LARGE_SMB_OFF_T */
385
386                 DEBUG (3, ("get_quota: Running command %s\n", syscmd));
387
388                 lines = file_lines_pload(syscmd, NULL);
389                 SAFE_FREE(syscmd);
390                 if (lines) {
391                         char *line = lines[0];
392
393                         DEBUG (3, ("Read output from set_quota, \"%s\"\n", line));
394
395                         TALLOC_FREE(lines);
396
397                         return 0;
398                 }
399                 DEBUG (0, ("set_quota_command failed!\n"));
400                 return -1;
401         }
402
403         errno = ENOSYS;
404         return -1;
405 }
406
407 int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
408 {
409         int ret = -1;
410         int i;
411         bool ready = False;
412         char *mntpath = NULL;
413         char *bdev = NULL;
414         char *fs = NULL;
415
416         if (!path||!dp)
417                 smb_panic("sys_get_quota: called with NULL pointer");
418
419         if (command_get_quota(path, qtype, id, dp)==0) {        
420                 return 0;
421         } else if (errno != ENOSYS) {
422                 return -1;
423         }
424
425         if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
426                 DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
427                 return ret;
428         }
429
430         errno = 0;
431         DEBUG(10,("sys_get_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
432
433         for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) {
434                 if (strcmp(fs,sys_quota_backends[i].name)==0) {
435                         ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp);
436                         if (ret!=0) {
437                                 DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
438                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
439                         } else {
440                                 DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
441                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
442                         }
443                         ready = True;
444                         break;  
445                 }               
446         }
447
448         if (!ready) {
449                 /* use the default vfs quota functions */
450                 ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp);
451                 if (ret!=0) {
452                         DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n",
453                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
454                 } else {
455                         DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
456                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
457                 }
458         }
459
460         SAFE_FREE(mntpath);
461         SAFE_FREE(bdev);
462         SAFE_FREE(fs);
463
464         if ((ret!=0)&& (errno == EDQUOT)) {
465                 DEBUG(10,("sys_get_quota() warning over quota!\n"));
466                 return 0;
467         }
468
469         return ret;
470 }
471
472 int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
473 {
474         int ret = -1;
475         int i;
476         bool ready = False;
477         char *mntpath = NULL;
478         char *bdev = NULL;
479         char *fs = NULL;
480
481         /* find the block device file */
482
483         if (!path||!dp)
484                 smb_panic("get_smb_quota: called with NULL pointer");
485
486         if (command_set_quota(path, qtype, id, dp)==0) {        
487                 return 0;
488         } else if (errno != ENOSYS) {
489                 return -1;
490         }
491
492         if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
493                 DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
494                 return ret;
495         }
496
497         errno = 0;
498         DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid())); 
499
500         for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) {
501                 if (strcmp(fs,sys_quota_backends[i].name)==0) {
502                         ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp);
503                         if (ret!=0) {
504                                 DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
505                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
506                         } else {
507                                 DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
508                                         fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
509                         }
510                         ready = True;
511                         break;
512                 }               
513         }
514
515         if (!ready) {
516                 /* use the default vfs quota functions */
517                 ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp);
518                 if (ret!=0) {
519                         DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
520                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
521                 } else {
522                         DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
523                                 "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
524                 }
525         }
526
527         SAFE_FREE(mntpath);
528         SAFE_FREE(bdev);
529         SAFE_FREE(fs);
530
531         if ((ret!=0)&& (errno == EDQUOT)) {
532                 DEBUG(10,("sys_set_quota() warning over quota!\n"));
533                 return 0;
534         }
535
536         return ret;             
537 }
538
539 #else /* HAVE_SYS_QUOTAS */
540  void dummy_sysquotas_c(void);
541
542  void dummy_sysquotas_c(void)
543 {
544         return;
545 }
546 #endif /* HAVE_SYS_QUOTAS */
547