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