source: trunk/libtransmission/platform.c @ 13705

Last change on this file since 13705 was 13705, checked in by livings124, 8 years ago

fixed implementation of r13701

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