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