source: trunk/libtransmission/platform.c @ 13697

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

(trunk, libT) #3833 'freespace' argument for 'session-get' RPC method -- apply taem's 0002-Add-XFS-quota-support.patch​ to add XFS quota support

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