source: trunk/libtransmission/platform.c @ 13715

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

(trunk, libT) #3833 'freespace rpc' 0001-Fix-FTBFS-for-OS-X-and-uClibc.patch

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