source: trunk/libtransmission/platform-quota.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

  • Property svn:keywords set to Date Rev Author Id
File size: 8.5 KB
Line 
1/*
2 * This file Copyright (C) 2013-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: platform-quota.c 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#include <errno.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <event2/util.h> /* evutil_ascii_strcasecmp () */
15
16#ifndef _WIN32
17 #include <unistd.h> /* getuid() */
18 #include <sys/types.h> /* types needed by quota.h */
19 #if defined(__FreeBSD__) || defined(__OpenBSD__)
20  #include <ufs/ufs/quota.h> /* quotactl() */
21 #elif defined (__NetBSD__)
22  #include <sys/param.h>
23  #ifndef statfs
24   #define statfs statvfs
25  #endif
26 #elif defined (__sun)
27  #include <sys/fs/ufs_quota.h> /* quotactl */
28 #else
29  #include <sys/quota.h> /* quotactl() */
30 #endif
31 #ifdef HAVE_GETMNTENT
32  #ifdef __sun
33   #include <fcntl.h>
34   #include <stdio.h>
35   #include <sys/mntent.h>
36   #include <sys/mnttab.h>
37   #define _PATH_MOUNTED MNTTAB
38  #else
39   #include <mntent.h>
40   #include <paths.h> /* _PATH_MOUNTED */
41  #endif
42 #else /* BSD derived systems */
43  #include <sys/param.h>
44  #include <sys/ucred.h>
45  #include <sys/mount.h>
46 #endif
47#endif
48
49#ifdef __APPLE__
50 #ifndef HAVE_SYS_STATVFS_H
51  #define HAVE_SYS_STATVFS_H
52 #endif
53 #ifndef HAVE_STATVFS
54  #define HAVE_STATVFS
55 #endif
56#endif
57
58#ifdef HAVE_SYS_STATVFS_H
59 #include <sys/statvfs.h>
60#endif
61
62#ifdef HAVE_XFS_XFS_H
63 #define HAVE_XQM
64 #include <xfs/xqm.h>
65#endif
66
67#include "transmission.h"
68#include "utils.h"
69#include "platform-quota.h"
70
71/***
72****
73***/
74
75#ifndef _WIN32
76static const char *
77getdev (const char * path)
78{
79#ifdef HAVE_GETMNTENT
80
81  FILE * fp;
82
83#ifdef __sun
84  struct mnttab mnt;
85  fp = fopen(_PATH_MOUNTED, "r");
86  if (fp == NULL)
87    return NULL;
88
89  while (getmntent(fp, &mnt))
90    if (tr_strcmp0 (path, mnt.mnt_mountp) == 0)
91      break;
92  fclose(fp);
93  return mnt.mnt_special;
94#else
95  struct mntent * mnt;
96
97  fp = setmntent(_PATH_MOUNTED, "r");
98  if (fp == NULL)
99    return NULL;
100
101  while ((mnt = getmntent(fp)) != NULL)
102    if (tr_strcmp0 (path, mnt->mnt_dir) == 0)
103      break;
104
105  endmntent(fp);
106  return mnt ? mnt->mnt_fsname : NULL;
107#endif
108#else /* BSD derived systems */
109
110  int i;
111  int n;
112  struct statfs * mnt;
113
114  n = getmntinfo(&mnt, MNT_WAIT);
115  if (!n)
116    return NULL;
117
118  for (i=0; i<n; i++)
119    if (tr_strcmp0 (path, mnt[i].f_mntonname) == 0)
120      break;
121
122  return (i < n) ? mnt[i].f_mntfromname : NULL;
123
124#endif
125}
126
127static const char *
128getfstype (const char * device)
129{
130
131#ifdef HAVE_GETMNTENT
132
133  FILE * fp;
134#ifdef __sun
135  struct mnttab mnt;
136  fp = fopen(_PATH_MOUNTED, "r");
137  if (fp == NULL)
138    return NULL;
139  while (getmntent(fp, &mnt))
140    if (tr_strcmp0 (device, mnt.mnt_mountp) == 0)
141      break;
142  fclose(fp);
143  return mnt.mnt_fstype;
144#else
145  struct mntent *mnt;
146
147  fp = setmntent (_PATH_MOUNTED, "r");
148  if (fp == NULL)
149    return NULL;
150
151  while ((mnt = getmntent (fp)) != NULL)
152    if (tr_strcmp0 (device, mnt->mnt_fsname) == 0)
153      break;
154
155  endmntent(fp);
156  return mnt ? mnt->mnt_type : NULL;
157#endif
158#else /* BSD derived systems */
159
160  int i;
161  int n;
162  struct statfs *mnt;
163
164  n = getmntinfo(&mnt, MNT_WAIT);
165  if (!n)
166    return NULL;
167
168  for (i=0; i<n; i++)
169    if (tr_strcmp0 (device, mnt[i].f_mntfromname) == 0)
170      break;
171
172  return (i < n) ? mnt[i].f_fstypename : NULL;
173
174#endif
175}
176
177static const char *
178getblkdev (const char * path)
179{
180  char * c;
181  char * dir;
182  const char * device;
183
184  dir = tr_strdup(path);
185
186  for (;;)
187    {
188      device = getdev (dir);
189      if (device != NULL)
190        break;
191
192      c = strrchr (dir, '/');
193      if (c != NULL)
194        *c = '\0';
195      else
196         break;
197    }
198
199  tr_free (dir);
200  return device;
201}
202
203#if defined(__NetBSD__) && (__NetBSD_Version__ >= 600000000)
204#include <quota.h>
205
206static int64_t
207getquota (const char * device)
208{
209  struct quotahandle *qh;
210  struct quotakey qk;
211  struct quotaval qv;
212  int64_t limit;
213  int64_t freespace;
214  int64_t spaceused;
215
216  qh = quota_open(device);
217  if (qh == NULL) {
218    return -1;
219  }
220  qk.qk_idtype = QUOTA_IDTYPE_USER;
221  qk.qk_id = getuid();
222  qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
223  if (quota_get(qh, &qk, &qv) == -1) {
224    quota_close(qh);
225    return -1;
226  }
227  if (qv.qv_softlimit > 0) {
228    limit = qv.qv_softlimit;
229  }
230  else if (qv.qv_hardlimit > 0) {
231    limit = qv.qv_hardlimit;
232  }
233  else {
234    quota_close(qh);
235    return -1;
236  }
237  spaceused = qv.qv_usage;
238  quota_close(qh);
239
240  freespace = limit - spaceused;
241  return (freespace < 0) ? 0 : freespace;
242}
243#else
244static int64_t
245getquota (const char * device)
246{
247  struct dqblk dq;
248  int64_t limit;
249  int64_t freespace;
250  int64_t spaceused;
251
252#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
253  if (quotactl(device, QCMD(Q_GETQUOTA, USRQUOTA), getuid(), (caddr_t) &dq) == 0)
254    {
255#elif defined(__sun)
256  struct quotctl  op;
257  int fd = open(device, O_RDONLY);
258  if (fd < 0)
259    return -1;
260  op.op = Q_GETQUOTA;
261  op.uid = getuid();
262  op.addr = (caddr_t) &dq;
263  if (ioctl(fd, Q_QUOTACTL, &op) == 0)
264    {
265      close(fd);
266#else
267  if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device, getuid(), (caddr_t) &dq) == 0)
268    {
269#endif
270      if (dq.dqb_bsoftlimit > 0)
271        {
272          /* Use soft limit first */
273          limit = dq.dqb_bsoftlimit;
274        }
275      else if (dq.dqb_bhardlimit > 0)
276        {
277          limit = dq.dqb_bhardlimit;
278        }
279      else
280        {
281          /* No quota enabled for this user */
282          return -1;
283        }
284#if defined(__FreeBSD__) || defined(__OpenBSD__)
285      spaceused = (int64_t) dq.dqb_curblocks >> 1;
286#elif defined(__APPLE__)
287      spaceused = (int64_t) dq.dqb_curbytes;
288#elif defined(__UCLIBC__)
289      spaceused = (int64_t) btodb(dq.dqb_curblocks);
290#elif defined(__sun) || (defined(_LINUX_QUOTA_VERSION) && _LINUX_QUOTA_VERSION < 2)
291      spaceused = (int64_t) dq.dqb_curblocks >> 1;
292#else
293      spaceused = btodb(dq.dqb_curspace);
294#endif
295      freespace = limit - spaceused;
296#ifdef __APPLE__
297      return (freespace < 0) ? 0 : freespace;
298#else
299      return (freespace < 0) ? 0 : freespace * 1024;
300#endif
301    }
302#if defined(__sun)
303  close(fd);
304#endif
305  /* something went wrong */
306  return -1;
307}
308#endif
309
310#ifdef HAVE_XQM
311static int64_t
312getxfsquota (char * device)
313{
314  int64_t limit;
315  int64_t freespace;
316  struct fs_disk_quota dq;
317
318  if (quotactl(QCMD(Q_XGETQUOTA, USRQUOTA), device, getuid(), (caddr_t) &dq) == 0)
319    {
320      if (dq.d_blk_softlimit > 0)
321        {
322          /* Use soft limit first */
323          limit = dq.d_blk_softlimit >> 1;
324        }
325      else if (dq.d_blk_hardlimit > 0)
326        {
327          limit = dq.d_blk_hardlimit >> 1;
328        }
329      else
330        {
331          /* No quota enabled for this user */
332          return -1;
333        }
334
335      freespace = limit - (dq.d_bcount >> 1);
336      return (freespace < 0) ? 0 : freespace * 1024;
337    }
338
339  /* something went wrong */
340  return -1;
341}
342#endif /* HAVE_XQM */
343#endif /* _WIN32 */
344
345static int64_t
346tr_getQuotaFreeSpace (const struct tr_device_info * info)
347{
348  int64_t ret = -1;
349
350#ifndef _WIN32
351
352  if (info->fstype && !evutil_ascii_strcasecmp(info->fstype, "xfs"))
353    {
354#ifdef HAVE_XQM
355      ret = getxfsquota (info->device);
356#endif
357    }
358  else
359    {
360      ret = getquota (info->device);
361    }
362
363#else /* _WIN32 */
364
365  (void) info;
366
367#endif /* _WIN32 */
368
369  return ret;
370}
371
372static int64_t
373tr_getDiskFreeSpace (const char * path)
374{
375#ifdef _WIN32
376
377  int64_t ret = -1;
378  wchar_t * wide_path;
379
380  wide_path = tr_win32_utf8_to_native (path, -1);
381
382  if (wide_path != NULL)
383    {
384      ULARGE_INTEGER freeBytesAvailable;
385      if (GetDiskFreeSpaceExW (wide_path, &freeBytesAvailable, NULL, NULL))
386        ret = freeBytesAvailable.QuadPart;
387
388      tr_free (wide_path);
389    }
390
391  return ret;
392
393#elif defined(HAVE_STATVFS)
394
395  struct statvfs buf;
396  return statvfs(path, &buf) ? -1 : (int64_t)buf.f_bavail * (int64_t)buf.f_frsize;
397
398#else
399
400  #warning FIXME: not implemented
401  return -1;
402
403#endif
404}
405
406struct tr_device_info *
407tr_device_info_create (const char * path)
408{
409  struct tr_device_info * info;
410
411  info = tr_new0 (struct tr_device_info, 1);
412  info->path = tr_strdup (path);
413#ifndef _WIN32
414  info->device = tr_strdup (getblkdev (path));
415  info->fstype = tr_strdup (getfstype (path));
416#endif
417
418  return info;
419}
420
421void
422tr_device_info_free (struct tr_device_info * info)
423{
424  if (info != NULL)
425    {
426      tr_free (info->fstype);
427      tr_free (info->device);
428      tr_free (info->path);
429      tr_free (info);
430    }
431}
432
433int64_t
434tr_device_info_get_free_space (const struct tr_device_info * info)
435{
436  int64_t free_space;
437
438  if ((info == NULL) || (info->path == NULL))
439    {
440      errno = EINVAL;
441      free_space = -1;
442    }
443  else
444    {
445      free_space = tr_getQuotaFreeSpace (info);
446
447      if (free_space < 0)
448        free_space = tr_getDiskFreeSpace (info->path);
449    }
450
451  return free_space;
452}
453
454/***
455****
456***/
Note: See TracBrowser for help on using the repository browser.