Coverage Report

Created: 2026-01-16 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/sysquotas.c
Line
Count
Source
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/util_file.h"
23
#include "lib/util/smb_strtox.h"
24
25
#undef DBGC_CLASS
26
0
#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
0
{
48
0
  int ret = -1;
49
0
  SMB_STRUCT_STAT S;
50
0
  FILE *fp;
51
0
  struct mntent *mnt = NULL;
52
0
  SMB_DEV_T devno;
53
0
  char *stat_mntpath = NULL;
54
0
  char *p;
55
56
  /* find the block device file */
57
0
  (*mntpath) = NULL;
58
0
  (*bdev) = NULL;
59
0
  (*fs) = NULL;
60
61
0
  if (sys_stat(path, &S, false) != 0) {
62
0
    return -1;
63
0
  }
64
65
0
  devno = S.st_ex_dev ;
66
67
0
  stat_mntpath = sys_realpath(path);
68
0
  if (stat_mntpath == NULL) {
69
0
    DBG_WARNING("realpath(%s) failed - %s\n", path,
70
0
          strerror(errno));
71
0
    goto out;
72
0
  }
73
74
0
  if (sys_stat(stat_mntpath, &S, false) != 0) {
75
0
    DBG_WARNING("cannot stat real path %s - %s\n", stat_mntpath,
76
0
          strerror(errno));
77
0
    goto out;
78
0
  }
79
80
0
  if (S.st_ex_dev != devno) {
81
0
    DBG_WARNING("device on real path has changed\n");
82
0
    goto out;
83
0
  }
84
85
0
  while (true) {
86
0
    char save_ch;
87
88
0
    p = strrchr(stat_mntpath, '/');
89
0
    if (p == NULL) {
90
0
      DBG_ERR("realpath for %s does not begin with a '/'\n",
91
0
        path);
92
0
      goto out;
93
0
    }
94
95
0
    if (p == stat_mntpath) {
96
0
      ++p;
97
0
    }
98
99
0
    save_ch = *p;
100
0
    *p = 0;
101
0
    if (sys_stat(stat_mntpath, &S, false) != 0) {
102
0
      DBG_WARNING("cannot stat real path component %s - %s\n",
103
0
            stat_mntpath, strerror(errno));
104
0
      goto out;
105
0
    }
106
0
    if (S.st_ex_dev != devno) {
107
0
      *p = save_ch;
108
0
      break;
109
0
    }
110
111
0
    if (p <= stat_mntpath + 1) {
112
0
      break;
113
0
    }
114
0
  }
115
116
0
  fp = setmntent(MOUNTED,"r");
117
0
  if (fp == NULL) {
118
0
    goto out;
119
0
  }
120
121
0
  while ((mnt = getmntent(fp))) {
122
0
    if (!strequal(mnt->mnt_dir, stat_mntpath)) {
123
0
      continue;
124
0
    }
125
126
0
    if ( sys_stat(mnt->mnt_dir, &S, false) == -1 )
127
0
      continue ;
128
129
0
    if (S.st_ex_dev == devno) {
130
0
      (*mntpath) = SMB_STRDUP(mnt->mnt_dir);
131
0
      (*bdev) = SMB_STRDUP(mnt->mnt_fsname);
132
0
      (*fs)   = SMB_STRDUP(mnt->mnt_type);
133
0
      if ((*mntpath)&&(*bdev)&&(*fs)) {
134
0
        ret = 0;
135
0
      } else {
136
0
        SAFE_FREE(*mntpath);
137
0
        SAFE_FREE(*bdev);
138
0
        SAFE_FREE(*fs);
139
0
        ret = -1;
140
0
      }
141
142
0
      break;
143
0
    }
144
0
  }
145
146
0
  endmntent(fp) ;
147
148
0
out:
149
0
  SAFE_FREE(stat_mntpath);
150
0
  return ret;
151
0
}
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
0
{
248
0
  const struct loadparm_substitution *lp_sub =
249
0
    loadparm_s3_global_substitution();
250
0
  const char *get_quota_command = NULL;
251
0
  char **lines = NULL;
252
0
  const char *p = NULL;
253
0
  char *p2 = NULL;
254
0
  int _id = -1;
255
0
  int error = 0;
256
0
  char **argl = NULL;
257
258
0
  get_quota_command = lp_get_quota_command(talloc_tos(), lp_sub);
259
0
  if ((get_quota_command == NULL) || (get_quota_command[0] == '\0')) {
260
0
    errno = ENOSYS;
261
0
    return -1;
262
0
  }
263
264
0
  switch(qtype) {
265
0
  case SMB_USER_QUOTA_TYPE:
266
0
  case SMB_USER_FS_QUOTA_TYPE:
267
0
    _id = id.uid;
268
0
    break;
269
0
  case SMB_GROUP_QUOTA_TYPE:
270
0
  case SMB_GROUP_FS_QUOTA_TYPE:
271
0
    _id = id.gid;
272
0
    break;
273
0
  default:
274
0
    DEBUG(0,("invalid quota type.\n"));
275
0
    return -1;
276
0
  }
277
278
0
  argl = str_list_make_empty(talloc_tos());
279
0
  str_list_add_printf(&argl, "%s", get_quota_command);
280
0
  str_list_add_printf(&argl, "%s", path);
281
0
  str_list_add_printf(&argl, "%d", qtype);
282
0
  str_list_add_printf(&argl, "%d", _id);
283
0
  if (argl == NULL) {
284
0
    return -1;
285
0
  }
286
287
0
  DBG_NOTICE("Running command %s %s %d %d\n",
288
0
       get_quota_command,
289
0
       path,
290
0
       qtype,
291
0
       _id);
292
293
0
  lines = file_lines_ploadv(talloc_tos(), argl, NULL);
294
0
  TALLOC_FREE(argl);
295
296
0
  if (lines) {
297
0
    char *line = lines[0];
298
299
0
    DEBUG (3, ("Read output from get_quota, \"%s\"\n", line));
300
301
    /* we need to deal with long long unsigned here, if supported */
302
303
0
    dp->qflags = smb_strtoul(line,
304
0
           &p2,
305
0
           10,
306
0
           &error,
307
0
           SMB_STR_STANDARD);
308
0
    if (error != 0) {
309
0
      goto invalid_param;
310
0
    }
311
312
0
    p = p2;
313
0
    while (p && *p && isspace(*p)) {
314
0
      p++;
315
0
    }
316
317
0
    if (p && *p) {
318
0
      dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p);
319
0
    } else {
320
0
      goto invalid_param;
321
0
    }
322
323
0
    while (p && *p && isspace(*p)) {
324
0
      p++;
325
0
    }
326
327
0
    if (p && *p) {
328
0
      dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p);
329
0
    } else {
330
0
      goto invalid_param;
331
0
    }
332
333
0
    while (p && *p && isspace(*p)) {
334
0
      p++;
335
0
    }
336
337
0
    if (p && *p) {
338
0
      dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p);
339
0
    } else {
340
0
      goto invalid_param;
341
0
    }
342
343
0
    while (p && *p && isspace(*p)) {
344
0
      p++;
345
0
    }
346
347
0
    if (p && *p) {
348
0
      dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p);
349
0
    } else {
350
0
      goto invalid_param;
351
0
    }
352
353
0
    while (p && *p && isspace(*p)) {
354
0
      p++;
355
0
    }
356
357
0
    if (p && *p) {
358
0
      dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p);
359
0
    } else {
360
0
      goto invalid_param;
361
0
    }
362
363
0
    while (p && *p && isspace(*p)) {
364
0
      p++;
365
0
    }
366
367
0
    if (p && *p) {
368
0
      dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p);
369
0
    } else {
370
0
      goto invalid_param;
371
0
    }
372
373
0
    while (p && *p && isspace(*p)) {
374
0
      p++;
375
0
    }
376
377
0
    if (p && *p) {
378
0
      dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL);
379
0
    } else {
380
0
      dp->bsize = 1024;
381
0
    }
382
383
0
    TALLOC_FREE(lines);
384
0
    lines = NULL;
385
386
0
    DBG_INFO("Parsed output of get_quota, ...\n"
387
0
       "qflags:%" PRIu32 " curblocks:%" PRIu64
388
0
       " softlimit:%" PRIu64 " hardlimit:%" PRIu64
389
0
       "\n"
390
0
       "curinodes:%" PRIu64 " isoftlimit:%" PRIu64
391
0
       " ihardlimit:%" PRIu64 " bsize:%" PRIu64 "\n",
392
0
       dp->qflags,
393
0
       dp->curblocks,
394
0
       dp->softlimit,
395
0
       dp->hardlimit,
396
0
       dp->curinodes,
397
0
       dp->isoftlimit,
398
0
       dp->ihardlimit,
399
0
       dp->bsize);
400
0
    return 0;
401
0
  }
402
403
0
  DEBUG (0, ("get_quota_command failed!\n"));
404
0
  return -1;
405
406
0
invalid_param:
407
408
0
  TALLOC_FREE(lines);
409
0
  DEBUG(0,("The output of get_quota_command is invalid!\n"));
410
0
  return -1;
411
0
}
412
413
static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
414
0
{
415
0
  const struct loadparm_substitution *lp_sub =
416
0
    loadparm_s3_global_substitution();
417
0
  const char *set_quota_command = NULL;
418
0
  char **lines = NULL;
419
0
  int _id = -1;
420
0
  char **argl = NULL;
421
422
0
  set_quota_command = lp_set_quota_command(talloc_tos(), lp_sub);
423
424
0
  if ((set_quota_command == NULL) || (set_quota_command[0] == '\0')) {
425
0
    errno = ENOSYS;
426
0
    return -1;
427
0
  }
428
429
0
  switch (qtype) {
430
0
  case SMB_USER_QUOTA_TYPE:
431
0
  case SMB_USER_FS_QUOTA_TYPE:
432
0
    _id = id.uid;
433
0
    break;
434
0
  case SMB_GROUP_QUOTA_TYPE:
435
0
  case SMB_GROUP_FS_QUOTA_TYPE:
436
0
    _id = id.gid;
437
0
    break;
438
0
  default:
439
0
    return -1;
440
0
  }
441
442
0
  argl = str_list_make_empty(talloc_tos());
443
0
  str_list_add_printf(&argl, "%s", set_quota_command);
444
0
  str_list_add_printf(&argl, "%s", path);
445
0
  str_list_add_printf(&argl, "%d", qtype);
446
0
  str_list_add_printf(&argl, "%d", _id);
447
0
  str_list_add_printf(&argl, "%u", dp->qflags);
448
0
  str_list_add_printf(&argl, "%" PRIu64, dp->softlimit);
449
0
  str_list_add_printf(&argl, "%" PRIu64, dp->hardlimit);
450
0
  str_list_add_printf(&argl, "%" PRIu64, dp->isoftlimit);
451
0
  str_list_add_printf(&argl, "%" PRIu64, dp->ihardlimit);
452
0
  str_list_add_printf(&argl, "%" PRIu64, dp->bsize);
453
0
  if (argl == NULL) {
454
0
    return -1;
455
0
  }
456
457
0
  DBG_NOTICE("Running command "
458
0
       "%s %s %d %d "
459
0
       "%" PRIu32 " %" PRIu64 " %" PRIu64 " "
460
0
       "%" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
461
0
       set_quota_command,
462
0
       path,
463
0
       qtype,
464
0
       _id,
465
0
       dp->qflags,
466
0
       dp->softlimit,
467
0
       dp->hardlimit,
468
0
       dp->isoftlimit,
469
0
       dp->ihardlimit,
470
0
       dp->bsize);
471
472
0
  lines = file_lines_ploadv(talloc_tos(), argl, NULL);
473
0
  TALLOC_FREE(argl);
474
0
  if (lines) {
475
0
    char *line = lines[0];
476
477
0
    DEBUG (3, ("Read output from set_quota, \"%s\"\n", line));
478
479
0
    TALLOC_FREE(lines);
480
481
0
    return 0;
482
0
  }
483
0
  DEBUG (0, ("set_quota_command failed!\n"));
484
0
  return -1;
485
0
}
486
487
int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
488
0
{
489
0
  int ret = -1;
490
0
  int i;
491
0
  bool ready = False;
492
0
  char *mntpath = NULL;
493
0
  char *bdev = NULL;
494
0
  char *fs = NULL;
495
496
0
  if (!path||!dp)
497
0
    smb_panic("sys_get_quota: called with NULL pointer");
498
499
0
  if (command_get_quota(path, qtype, id, dp)==0) {
500
0
    return 0;
501
0
  } else if (errno != ENOSYS) {
502
0
    return -1;
503
0
  }
504
505
0
  if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
506
0
    DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
507
0
    return ret;
508
0
  }
509
510
0
  errno = 0;
511
0
  DEBUG(10,("sys_get_quota() uid(%u, %u), fs(%s)\n", (unsigned)getuid(), (unsigned)geteuid(), fs));
512
513
0
  for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) {
514
0
    if (strcmp(fs,sys_quota_backends[i].name)==0) {
515
0
      ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp);
516
0
      if (ret!=0) {
517
0
        DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
518
0
          fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
519
0
      } else {
520
0
        DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
521
0
          fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
522
0
      }
523
0
      ready = True;
524
0
      break;
525
0
    }
526
0
  }
527
528
0
  if (!ready) {
529
    /* use the default vfs quota functions */
530
0
    ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp);
531
0
    if (ret!=0) {
532
0
      DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n",
533
0
        "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
534
0
    } else {
535
0
      DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
536
0
        "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
537
0
    }
538
0
  }
539
540
0
  SAFE_FREE(mntpath);
541
0
  SAFE_FREE(bdev);
542
0
  SAFE_FREE(fs);
543
544
0
  return ret;
545
0
}
546
547
int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
548
0
{
549
0
  int ret = -1;
550
0
  int i;
551
0
  bool ready = False;
552
0
  char *mntpath = NULL;
553
0
  char *bdev = NULL;
554
0
  char *fs = NULL;
555
556
  /* find the block device file */
557
558
0
  if (!path||!dp)
559
0
    smb_panic("get_smb_quota: called with NULL pointer");
560
561
0
  if (command_set_quota(path, qtype, id, dp)==0) {
562
0
    return 0;
563
0
  } else if (errno != ENOSYS) {
564
0
    return -1;
565
0
  }
566
567
0
  if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
568
0
    DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
569
0
    return ret;
570
0
  }
571
572
0
  errno = 0;
573
0
  DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
574
575
0
  for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) {
576
0
    if (strcmp(fs,sys_quota_backends[i].name)==0) {
577
0
      ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp);
578
0
      if (ret!=0) {
579
0
        DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
580
0
          fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
581
0
      } else {
582
0
        DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
583
0
          fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
584
0
      }
585
0
      ready = True;
586
0
      break;
587
0
    }
588
0
  }
589
590
0
  if (!ready) {
591
    /* use the default vfs quota functions */
592
0
    ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp);
593
0
    if (ret!=0) {
594
0
      DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
595
0
        "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
596
0
    } else {
597
0
      DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
598
0
        "vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
599
0
    }
600
0
  }
601
602
0
  SAFE_FREE(mntpath);
603
0
  SAFE_FREE(bdev);
604
0
  SAFE_FREE(fs);
605
606
0
  return ret;
607
0
}
608
609
#else /* HAVE_SYS_QUOTAS */
610
 void dummy_sysquotas_c(void);
611
612
 void dummy_sysquotas_c(void)
613
{
614
  return;
615
}
616
#endif /* HAVE_SYS_QUOTAS */