source: trunk/libtransmission/platform.c @ 13991

Last change on this file since 13991 was 13991, checked in by jordan, 8 years ago

(trunk) first draft of changing the FreeSpace? API to behave as https://trac.transmissionbt.com/ticket/4076#comment:25 -- libT, rpc, qt, and gtk implementations.

  • Property svn:keywords set to Date Rev Author Id
File size: 25.0 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: platform.c 13991 2013-02-09 04:05:03Z jordan $
11 */
12
13#ifndef WIN32
14 #include <sys/types.h> /* types needed by quota.h */
15 #ifdef __FreeBSD__
16  #include <ufs/ufs/quota.h> /* quotactl() */
17 #elif defined (__sun)
18  #include <sys/fs/ufs_quota.h> /* quotactl */
19 #else
20  #include <sys/quota.h> /* quotactl() */
21 #endif
22 #ifdef HAVE_GETMNTENT
23  #ifdef __sun
24   #include <stdio.h>
25   #include <sys/mntent.h>
26   #include <sys/mnttab.h>
27   #define _PATH_MOUNTED MNTTAB
28  #else
29   #include <mntent.h>
30   #include <paths.h> /* _PATH_MOUNTED */
31  #endif
32 #else /* BSD derived systems */
33  #include <sys/param.h>
34  #include <sys/ucred.h>
35  #include <sys/mount.h>
36 #endif
37#endif
38
39#ifdef WIN32
40 #include <w32api.h>
41 #define WINVER  WindowsXP
42 #include <windows.h>
43 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
44#else
45 #ifdef SYS_DARWIN
46  #include <CoreFoundation/CoreFoundation.h>
47 #endif
48 #ifdef __HAIKU__
49  #include <FindDirectory.h>
50 #endif
51 #define _XOPEN_SOURCE 600  /* needed for recursive locks. */
52 #ifndef __USE_UNIX98
53  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
54 #endif
55 #include <pthread.h>
56#endif
57
58#include <assert.h>
59#include <errno.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63
64#ifdef SYS_DARWIN
65 #define HAVE_SYS_STATVFS_H
66 #define HAVE_STATVFS
67#endif
68
69#include <sys/stat.h>
70#ifdef HAVE_SYS_STATVFS_H
71 #include <sys/statvfs.h>
72#endif
73#ifdef WIN32
74#include <libgen.h>
75#endif
76#include <dirent.h>
77#include <fcntl.h>
78#include <unistd.h> /* getuid getpid close */
79
80#ifdef HAVE_XFS_XFS_H
81 #define HAVE_XQM
82 #include <xfs/xqm.h>
83#endif
84
85#include <event2/util.h> /* evutil_ascii_strcasecmp () */
86
87#include "transmission.h"
88#include "session.h"
89#include "list.h"
90#include "log.h"
91#include "platform.h"
92#include "utils.h"
93
94/***
95****  THREADS
96***/
97
98#ifdef WIN32
99typedef DWORD tr_thread_id;
100#else
101typedef pthread_t tr_thread_id;
102#endif
103
104static tr_thread_id
105tr_getCurrentThread (void)
106{
107#ifdef WIN32
108  return GetCurrentThreadId ();
109#else
110  return pthread_self ();
111#endif
112}
113
114static bool
115tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
116{
117#ifdef WIN32
118  return a == b;
119#else
120  return pthread_equal (a, b) != 0;
121#endif
122}
123
124/** @brief portability wrapper around OS-dependent threads */
125struct tr_thread
126{
127  void          (* func)(void *);
128  void           * arg;
129  tr_thread_id     thread;
130#ifdef WIN32
131  HANDLE           thread_handle;
132#endif
133};
134
135bool
136tr_amInThread (const tr_thread * t)
137{
138  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
139}
140
141#ifdef WIN32
142 #define ThreadFuncReturnType unsigned WINAPI
143#else
144 #define ThreadFuncReturnType void
145#endif
146
147static ThreadFuncReturnType
148ThreadFunc (void * _t)
149{
150  tr_thread * t = _t;
151
152  t->func (t->arg);
153
154  tr_free (t);
155#ifdef WIN32
156  _endthreadex (0);
157  return 0;
158#endif
159}
160
161tr_thread *
162tr_threadNew (void (*func)(void *), void * arg)
163{
164  tr_thread * t = tr_new0 (tr_thread, 1);
165
166  t->func = func;
167  t->arg  = arg;
168
169#ifdef WIN32
170  {
171    unsigned int id;
172    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
173    t->thread = (DWORD) id;
174  }
175#else
176  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
177  pthread_detach (t->thread);
178#endif
179
180  return t;
181}
182
183/***
184****  LOCKS
185***/
186
187/** @brief portability wrapper around OS-dependent thread mutexes */
188struct tr_lock
189{
190  int                 depth;
191#ifdef WIN32
192  CRITICAL_SECTION    lock;
193  DWORD               lockThread;
194#else
195  pthread_mutex_t     lock;
196  pthread_t           lockThread;
197#endif
198};
199
200tr_lock*
201tr_lockNew (void)
202{
203  tr_lock * l = tr_new0 (tr_lock, 1);
204
205#ifdef WIN32
206  InitializeCriticalSection (&l->lock); /* supports recursion */
207#else
208  pthread_mutexattr_t attr;
209  pthread_mutexattr_init (&attr);
210  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
211  pthread_mutex_init (&l->lock, &attr);
212#endif
213
214  return l;
215}
216
217void
218tr_lockFree (tr_lock * l)
219{
220#ifdef WIN32
221    DeleteCriticalSection (&l->lock);
222#else
223    pthread_mutex_destroy (&l->lock);
224#endif
225    tr_free (l);
226}
227
228void
229tr_lockLock (tr_lock * l)
230{
231#ifdef WIN32
232  EnterCriticalSection (&l->lock);
233#else
234  pthread_mutex_lock (&l->lock);
235#endif
236
237  assert (l->depth >= 0);
238  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
239  l->lockThread = tr_getCurrentThread ();
240  ++l->depth;
241}
242
243int
244tr_lockHave (const tr_lock * l)
245{
246  return (l->depth > 0) &&
247         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
248}
249
250void
251tr_lockUnlock (tr_lock * l)
252{
253  assert (l->depth > 0);
254  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
255
256  --l->depth;
257  assert (l->depth >= 0);
258#ifdef WIN32
259  LeaveCriticalSection (&l->lock);
260#else
261  pthread_mutex_unlock (&l->lock);
262#endif
263}
264
265/***
266****  PATHS
267***/
268
269#ifndef WIN32
270 #include <pwd.h>
271#endif
272
273static const char *
274getHomeDir (void)
275{
276  static char * home = NULL;
277
278  if (!home)
279    {
280      home = tr_strdup (getenv ("HOME"));
281
282      if (!home)
283        {
284#ifdef WIN32
285          char appdata[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
286          *appdata = '\0';
287          SHGetFolderPath (NULL, CSIDL_PERSONAL, NULL, 0, appdata);
288          home = tr_strdup (appdata);
289#else
290          struct passwd * pw = getpwuid (getuid ());
291          if (pw)
292            home = tr_strdup (pw->pw_dir);
293          endpwent ();
294#endif
295        }
296
297      if (!home)
298        home = tr_strdup ("");
299    }
300
301  return home;
302}
303
304static const char *
305getOldConfigDir (void)
306{
307  static char * path = NULL;
308
309  if (!path)
310    {
311#ifdef SYS_DARWIN
312      path = tr_buildPath (getHomeDir (), "Library",
313                           "Application Support",
314                           "Transmission", NULL);
315#elif defined (WIN32)
316      char appdata[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
317      SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, appdata);
318      path = tr_buildPath (appdata, "Transmission", NULL);
319#elif defined (__HAIKU__)
320      char buf[TR_PATH_MAX];
321      find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
322      path = tr_buildPath (buf, "Transmission", NULL);
323#else
324      path = tr_buildPath (getHomeDir (), ".transmission", NULL);
325#endif
326    }
327
328  return path;
329}
330
331#if defined (SYS_DARWIN) || defined (WIN32)
332 #define RESUME_SUBDIR  "Resume"
333 #define TORRENT_SUBDIR "Torrents"
334#else
335 #define RESUME_SUBDIR  "resume"
336 #define TORRENT_SUBDIR "torrents"
337#endif
338
339static const char *
340getOldTorrentsDir (void)
341{
342  static char * path = NULL;
343
344  if (!path)
345    path = tr_buildPath (getOldConfigDir (), TORRENT_SUBDIR, NULL);
346
347  return path;
348}
349
350static const char *
351getOldCacheDir (void)
352{
353  static char * path = NULL;
354
355  if (!path)
356    {
357#if defined (WIN32)
358      path = tr_buildPath (getOldConfigDir (), "Cache", NULL);
359#elif defined (SYS_DARWIN)
360      path = tr_buildPath (getHomeDir (), "Library", "Caches", "Transmission", NULL);
361#else
362      path = tr_buildPath (getOldConfigDir (), "cache", NULL);
363#endif
364    }
365
366  return path;
367}
368
369static void
370moveFiles (const char * oldDir, const char * newDir)
371{
372  if (oldDir && newDir && strcmp (oldDir, newDir))
373    {
374      DIR * dirh = opendir (oldDir);
375      if (dirh)
376        {
377          int count = 0;
378          struct dirent * dirp;
379          while ((dirp = readdir (dirh)))
380            {
381              const char * name = dirp->d_name;
382              if (name && strcmp (name, ".") && strcmp (name, ".."))
383                {
384                  char * o = tr_buildPath (oldDir, name, NULL);
385                  char * n = tr_buildPath (newDir, name, NULL);
386                  tr_rename (o, n);
387                  ++count;
388                  tr_free (n);
389                  tr_free (o);
390                }
391            }
392
393          if (count)
394            tr_logAddInfo (_("Migrated %1$d files from \"%2$s\" to \"%3$s\""), count, oldDir, newDir);
395
396          closedir (dirh);
397        }
398    }
399}
400
401/**
402 * This function is for transmission-gtk users to migrate the config files
403 * from $HOME/.transmission/ (where they were kept before Transmission 1.30)
404 * to $HOME/.config/$appname as per the XDG directory spec.
405 */
406static void
407migrateFiles (const tr_session * session)
408{
409  static int migrated = false;
410  const bool should_migrate = strstr (getOldConfigDir (), ".transmission") != NULL;
411
412  if (!migrated && should_migrate)
413    {
414      const char * oldDir;
415      const char * newDir;
416
417      migrated = true;
418
419      oldDir = getOldTorrentsDir ();
420      newDir = tr_getTorrentDir (session);
421      moveFiles (oldDir, newDir);
422
423      oldDir = getOldCacheDir ();
424      newDir = tr_getResumeDir (session);
425      moveFiles (oldDir, newDir);
426    }
427}
428
429void
430tr_setConfigDir (tr_session * session, const char * configDir)
431{
432  char * path;
433
434  session->configDir = tr_strdup (configDir);
435
436  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
437  tr_mkdirp (path, 0777);
438  session->resumeDir = path;
439
440  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
441  tr_mkdirp (path, 0777);
442  session->torrentDir = path;
443
444  migrateFiles (session);
445}
446
447const char *
448tr_sessionGetConfigDir (const tr_session * session)
449{
450  return session->configDir;
451}
452
453const char *
454tr_getTorrentDir (const tr_session * session)
455{
456  return session->torrentDir;
457}
458
459const char *
460tr_getResumeDir (const tr_session * session)
461{
462  return session->resumeDir;
463}
464
465const char*
466tr_getDefaultConfigDir (const char * appname)
467{
468  static char * s = NULL;
469
470  if (!appname || !*appname)
471    appname = "Transmission";
472
473  if (!s)
474    {
475      if ((s = getenv ("TRANSMISSION_HOME")))
476        {
477          s = tr_strdup (s);
478        }
479        else
480        {
481#ifdef SYS_DARWIN
482          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
483#elif defined (WIN32)
484          char appdata[TR_PATH_MAX]; /* SHGetFolderPath () requires MAX_PATH */
485          SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, appdata);
486          s = tr_buildPath (appdata, appname, NULL);
487#elif defined (__HAIKU__)
488          char buf[TR_PATH_MAX];
489          find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
490          s = tr_buildPath (buf, appname, NULL);
491#else
492          if ((s = getenv ("XDG_CONFIG_HOME")))
493            s = tr_buildPath (s, appname, NULL);
494          else
495            s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
496#endif
497        }
498    }
499
500  return s;
501}
502
503const char*
504tr_getDefaultDownloadDir (void)
505{
506  static char * user_dir = NULL;
507
508  if (user_dir == NULL)
509    {
510      const char * config_home;
511      char * config_file;
512      char * content;
513      size_t content_len;
514
515      /* figure out where to look for user-dirs.dirs */
516      config_home = getenv ("XDG_CONFIG_HOME");
517      if (config_home && *config_home)
518        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
519      else
520        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
521
522      /* read in user-dirs.dirs and look for the download dir entry */
523      content = (char *) tr_loadFile (config_file, &content_len);
524      if (content && content_len>0)
525        {
526          const char * key = "XDG_DOWNLOAD_DIR=\"";
527          char * line = strstr (content, key);
528          if (line != NULL)
529            {
530              char * value = line + strlen (key);
531              char * end = strchr (value, '"');
532
533              if (end)
534                {
535                  *end = '\0';
536
537                  if (!memcmp (value, "$HOME/", 6))
538                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
539                  else if (!strcmp (value, "$HOME"))
540                    user_dir = tr_strdup (getHomeDir ());
541                  else
542                    user_dir = tr_strdup (value);
543                }
544            }
545        }
546
547      if (user_dir == NULL)
548#ifdef __HAIKU__
549        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
550#else
551        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
552#endif
553
554      tr_free (content);
555      tr_free (config_file);
556    }
557
558  return user_dir;
559}
560
561/***
562****
563***/
564
565static int
566isWebClientDir (const char * path)
567{
568  struct stat sb;
569  char * tmp = tr_buildPath (path, "index.html", NULL);
570  const int ret = !stat (tmp, &sb);
571  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
572  tr_free (tmp);
573
574  return ret;
575}
576
577const char *
578tr_getWebClientDir (const tr_session * session UNUSED)
579{
580  static char * s = NULL;
581
582  if (!s)
583    {
584      if ((s = getenv ("CLUTCH_HOME")))
585        {
586          s = tr_strdup (s);
587        }
588      else if ((s = getenv ("TRANSMISSION_WEB_HOME")))
589        {
590          s = tr_strdup (s);
591        }
592      else
593        {
594
595#ifdef SYS_DARWIN /* on Mac, look in the Application Support folder first, then in the app bundle. */
596
597          /* Look in the Application Support folder */
598          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
599
600          if (!isWebClientDir (s))
601            {
602              tr_free (s);
603
604              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
605              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
606                                                            kCFURLPOSIXPathStyle);
607              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
608
609              char * appString = tr_malloc (appStringLength);
610              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
611              assert (success);
612
613              CFRelease (appURL);
614              CFRelease (appRef);
615
616              /* Fallback to the app bundle */
617              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
618              if (!isWebClientDir (s))
619                {
620                  tr_free (s);
621                  s = NULL;
622                }
623
624              tr_free (appString);
625            }
626
627#elif defined (WIN32)
628
629          /* SHGetFolderPath explicitly requires MAX_PATH length */
630          char dir[MAX_PATH];
631
632          /* Generally, Web interface should be stored in a Web subdir of
633           * calling executable dir. */
634
635          if (s == NULL) /* check personal AppData/Transmission/Web */
636            {
637              SHGetFolderPath (NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir);
638              s = tr_buildPath (dir, "Transmission", "Web", NULL);
639              if (!isWebClientDir (s))
640                {
641                  tr_free (s);
642                  s = NULL;
643                }
644            }
645
646          if (s == NULL) /* check personal AppData */
647            {
648              SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, dir);
649              s = tr_buildPath (dir, "Transmission", "Web", NULL);
650              if (!isWebClientDir (s))
651                {
652                  tr_free (s);
653                  s = NULL;
654                }
655            }
656
657            if (s == NULL) /* check calling module place */
658              {
659                GetModuleFileName (GetModuleHandle (NULL), dir, sizeof (dir));
660                s = tr_buildPath (dirname (dir), "Web", NULL);
661                if (!isWebClientDir (s))
662                  {
663                    tr_free (s);
664                    s = NULL;
665                  }
666            }
667
668#else /* everyone else, follow the XDG spec */
669
670          tr_list *candidates = NULL, *l;
671          const char * tmp;
672
673          /* XDG_DATA_HOME should be the first in the list of candidates */
674          tmp = getenv ("XDG_DATA_HOME");
675          if (tmp && *tmp)
676            {
677              tr_list_append (&candidates, tr_strdup (tmp));
678            }
679          else
680            {
681              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
682              tr_list_append (&candidates, dhome);
683            }
684
685          /* XDG_DATA_DIRS are the backup directories */
686          {
687            const char * pkg = PACKAGE_DATA_DIR;
688            const char * xdg = getenv ("XDG_DATA_DIRS");
689            const char * fallback = "/usr/local/share:/usr/share";
690            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
691            tmp = buf;
692            while (tmp && *tmp)
693              {
694                const char * end = strchr (tmp, ':');
695                if (end)
696                  {
697                    if ((end - tmp) > 1)
698                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
699                    tmp = end + 1;
700                  }
701                else if (tmp && *tmp)
702                  {
703                    tr_list_append (&candidates, tr_strdup (tmp));
704                    break;
705                  }
706              }
707            tr_free (buf);
708          }
709
710          /* walk through the candidates & look for a match */
711          for (l=candidates; l; l=l->next)
712            {
713              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
714              const int found = isWebClientDir (path);
715              if (found)
716                {
717                  s = path;
718                  break;
719                }
720              tr_free (path);
721            }
722
723          tr_list_free (&candidates, tr_free);
724
725#endif
726
727        }
728    }
729
730  return s;
731}
732
733/***
734****
735***/
736
737#ifndef WIN32
738static const char *
739getdev (const char * path)
740{
741#ifdef HAVE_GETMNTENT
742
743  FILE * fp;
744
745#ifdef __sun
746  struct mnttab * mnt;
747  fp = fopen(_PATH_MOUNTED, "r");
748  if (fp == NULL)
749    return NULL;
750
751  while (getmntent(fp, mnt))
752    if (!tr_strcmp0 (path, mnt->mnt_mountp))
753      break;
754  fclose(fp);
755  return mnt ? mnt->mnt_fstype : NULL;
756#else
757  struct mntent * mnt;
758
759  fp = setmntent(_PATH_MOUNTED, "r");
760  if (fp == NULL)
761    return NULL;
762
763  while ((mnt = getmntent(fp)) != NULL)
764    if (!tr_strcmp0 (path, mnt->mnt_dir))
765      break;
766
767  endmntent(fp);
768  return mnt ? mnt->mnt_fsname : NULL;
769#endif
770#else /* BSD derived systems */
771
772  int i;
773  int n;
774  struct statfs * mnt;
775
776  n = getmntinfo(&mnt, MNT_WAIT);
777  if (!n)
778    return NULL;
779
780  for (i=0; i<n; i++)
781    if (!tr_strcmp0 (path, mnt[i].f_mntonname))
782      break;
783
784  return (i < n) ? mnt[i].f_mntfromname : NULL;
785
786#endif
787}
788
789static const char *
790getfstype (const char * device)
791{
792
793#ifdef HAVE_GETMNTENT
794
795  FILE * fp;
796#ifdef __sun
797  struct mnttab *mnt;
798  fp = fopen(_PATH_MOUNTED, "r");
799  if (fp == NULL)
800    return NULL;
801  while (getmntent(fp, mnt))
802    if (!tr_strcmp0 (device, mnt->mnt_mountp))
803      break;
804  fclose(fp);
805  return mnt ? mnt->mnt_fstype : NULL;
806#else
807  struct mntent *mnt;
808
809  fp = setmntent (_PATH_MOUNTED, "r");
810  if (fp == NULL)
811    return NULL;
812
813  while ((mnt = getmntent (fp)) != NULL)
814    if (!tr_strcmp0 (device, mnt->mnt_fsname))
815      break;
816
817  endmntent(fp);
818  return mnt ? mnt->mnt_type : NULL;
819#endif
820#else /* BSD derived systems */
821
822  int i;
823  int n;
824  struct statfs *mnt;
825
826  n = getmntinfo(&mnt, MNT_WAIT);
827  if (!n)
828    return NULL;
829
830  for (i=0; i<n; i++)
831    if (!tr_strcmp0 (device, mnt[i].f_mntfromname))
832      break;
833
834  return (i < n) ? mnt[i].f_fstypename : NULL;
835
836#endif
837}
838
839static const char *
840getblkdev (const char * path)
841{
842  char * c;
843  char * dir;
844  const char * device;
845
846  dir = tr_strdup(path);
847
848  for (;;)
849    {
850      device = getdev (dir);
851      if (device != NULL)
852        break;
853
854      c = strrchr (dir, '/');
855      if (c != NULL)
856        *c = '\0';
857      else
858         break;
859    }
860
861  tr_free (dir);
862  return device;
863}
864
865static int64_t
866getquota (const char * device)
867{
868  struct dqblk dq;
869  int64_t limit;
870  int64_t freespace;
871  int64_t spaceused;
872
873#if defined(__FreeBSD__) || defined(SYS_DARWIN)
874  if (quotactl(device, QCMD(Q_GETQUOTA, USRQUOTA), getuid(), (caddr_t) &dq) == 0)
875    {
876#elif defined(__sun)
877  struct quotctl  op; 
878  int fd = open(device, O_RDONLY); 
879  if (fd < 0) 
880    return -1; 
881  op.op = Q_GETQUOTA; 
882  op.uid = getuid(); 
883  op.addr = (caddr_t) &dq; 
884  if (ioctl(fd, Q_QUOTACTL, &op) == 0)
885    {
886      close(fd);
887#else
888  if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device, getuid(), (caddr_t) &dq) == 0)
889    {
890#endif
891      if (dq.dqb_bsoftlimit > 0)
892        {
893          /* Use soft limit first */
894          limit = dq.dqb_bsoftlimit;
895        }
896      else if (dq.dqb_bhardlimit > 0)
897        {
898          limit = dq.dqb_bhardlimit;
899        }
900      else
901        {
902          /* No quota enabled for this user */
903          return -1;
904        }
905#if defined(__FreeBSD__)
906      spaceused = (int64_t) dq.dqb_curblocks >> 1;
907#elif defined(SYS_DARWIN)
908      spaceused = (int64_t) dq.dqb_curbytes;
909#elif defined(__UCLIBC__)
910      spaceused = (int64_t) btodb(dq.dqb_curblocks);
911#elif defined(__sun)
912      spaceused = (int64_t) dq.dqb_curblocks >> 1;
913#else
914      spaceused = btodb(dq.dqb_curspace);
915#endif
916      freespace = limit - spaceused;
917#ifdef SYS_DARWIN
918      return (freespace < 0) ? 0 : freespace;
919#else
920      return (freespace < 0) ? 0 : freespace * 1024;
921#endif
922    }
923#if defined(__sun)
924  close(fd);
925#endif
926  /* something went wrong */
927  return -1;
928}
929
930#ifdef HAVE_XQM
931static int64_t
932getxfsquota (char * device)
933{
934  int64_t limit;
935  int64_t freespace;
936  struct fs_disk_quota dq;
937
938  if (quotactl(QCMD(Q_XGETQUOTA, USRQUOTA), device, getuid(), (caddr_t) &dq) == 0)
939    {
940      if (dq.d_blk_softlimit > 0)
941        {
942          /* Use soft limit first */
943          limit = dq.d_blk_softlimit >> 1;
944        }
945      else if (dq.d_blk_hardlimit > 0)
946        {
947          limit = dq.d_blk_hardlimit >> 1;
948        }
949      else
950        {
951          /* No quota enabled for this user */
952          return -1;
953        }
954
955      freespace = limit - (dq.d_bcount >> 1);
956      return (freespace < 0) ? 0 : freespace * 1024;
957    }
958
959  /* something went wrong */
960  return -1;
961}
962#endif /* HAVE_XQM */
963#endif /* WIN32 */
964
965static int64_t
966tr_getQuotaFreeSpace (const struct tr_device_info * info)
967{
968  int64_t ret = -1;
969
970#ifndef WIN32
971
972  if (info->fstype && !evutil_ascii_strcasecmp(info->fstype, "xfs"))
973    {
974#ifdef HAVE_XQM
975      ret = getxfsquota (info->device);
976#endif
977    }
978  else
979    {
980      ret = getquota (info->device);
981    }
982#endif /* WIN32 */
983
984  return ret;
985}
986
987static int64_t
988tr_getDiskFreeSpace (const char * path)
989{
990#ifdef WIN32
991
992  uint64_t freeBytesAvailable = 0;
993  return GetDiskFreeSpaceEx (path, &freeBytesAvailable, NULL, NULL)
994    ? (int64_t)freeBytesAvailable
995    : -1;
996
997#elif defined(HAVE_STATVFS)
998
999  struct statvfs buf;
1000  return statvfs(path, &buf) ? -1 : (int64_t)buf.f_bavail * (int64_t)buf.f_frsize;
1001
1002#else
1003
1004  #warning FIXME: not implemented
1005  return -1;
1006
1007#endif
1008}
1009
1010struct tr_device_info *
1011tr_device_info_create (const char * path)
1012{
1013  struct tr_device_info * info;
1014
1015  info = tr_new0 (struct tr_device_info, 1);
1016  info->path = tr_strdup (path);
1017  info->device = tr_strdup (getblkdev (path));
1018  info->fstype = tr_strdup (getfstype (path));
1019
1020  return info;
1021}
1022
1023void
1024tr_device_info_free (struct tr_device_info * info)
1025{
1026  if (info != NULL)
1027    {
1028      tr_free (info->fstype);
1029      tr_free (info->device);
1030      tr_free (info->path);
1031      tr_free (info);
1032    }
1033}
1034
1035int64_t
1036tr_device_info_get_free_space (const struct tr_device_info * info)
1037{
1038  int64_t free_space;
1039
1040  if ((info == NULL) || (info->path == NULL))
1041    {
1042      errno = EINVAL;
1043      free_space = -1;
1044    }
1045  else
1046    {
1047      free_space = tr_getQuotaFreeSpace (info);
1048
1049      if (free_space < 0)
1050        free_space = tr_getDiskFreeSpace (info->path);
1051    }
1052
1053  return free_space;
1054}
1055
1056/***
1057****
1058***/
1059
1060#ifdef WIN32
1061
1062/* The following mmap functions are by Joerg Walter, and were taken from
1063 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm */
1064
1065#if defined (_MSC_VER)
1066__declspec (align (4)) static LONG volatile g_sl;
1067#else
1068static LONG volatile g_sl __attribute__((aligned (4)));
1069#endif
1070
1071/* Wait for spin lock */
1072static int
1073slwait (LONG volatile *sl)
1074{
1075  while (InterlockedCompareExchange (sl, 1, 0) != 0)
1076    Sleep (0);
1077
1078  return 0;
1079}
1080
1081/* Release spin lock */
1082static int
1083slrelease (LONG volatile *sl)
1084{
1085  InterlockedExchange (sl, 0);
1086  return 0;
1087}
1088
1089/* getpagesize for windows */
1090static long
1091getpagesize (void)
1092{
1093  static long g_pagesize = 0;
1094
1095  if (!g_pagesize)
1096    {
1097      SYSTEM_INFO system_info;
1098      GetSystemInfo (&system_info);
1099      g_pagesize = system_info.dwPageSize;
1100    }
1101
1102  return g_pagesize;
1103}
1104
1105static long
1106getregionsize (void)
1107{
1108  static long g_regionsize = 0;
1109
1110  if (!g_regionsize)
1111    {
1112      SYSTEM_INFO system_info;
1113      GetSystemInfo (&system_info);
1114      g_regionsize = system_info.dwAllocationGranularity;
1115    }
1116
1117  return g_regionsize;
1118}
1119
1120void *
1121mmap (void *ptr, long  size, long  prot, long  type, long  handle, long  arg)
1122{
1123  static long g_pagesize;
1124  static long g_regionsize;
1125
1126  /* Wait for spin lock */
1127  slwait (&g_sl);
1128
1129  /* First time initialization */
1130  if (!g_pagesize)
1131    g_pagesize = getpagesize ();
1132  if (!g_regionsize)
1133    g_regionsize = getregionsize ();
1134
1135  /* Allocate this */
1136  ptr = VirtualAlloc (ptr, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
1137  if (!ptr)
1138    {
1139      ptr = (void *) -1;
1140      goto mmap_exit;
1141    }
1142
1143mmap_exit:
1144  /* Release spin lock */
1145  slrelease (&g_sl);
1146  return ptr;
1147}
1148
1149long
1150munmap (void *ptr, long size)
1151{
1152  static long g_pagesize;
1153  static long g_regionsize;
1154  int rc = -1;
1155
1156  /* Wait for spin lock */
1157  slwait (&g_sl);
1158
1159  /* First time initialization */
1160  if (!g_pagesize)
1161    g_pagesize = getpagesize ();
1162  if (!g_regionsize)
1163    g_regionsize = getregionsize ();
1164
1165  /* Free this */
1166  if (!VirtualFree (ptr, 0, MEM_RELEASE))
1167    goto munmap_exit;
1168
1169  rc = 0;
1170
1171munmap_exit:
1172  /* Release spin lock */
1173  slrelease (&g_sl);
1174  return rc;
1175}
1176
1177#endif
Note: See TracBrowser for help on using the repository browser.