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