source: trunk/libtransmission/platform.c @ 13716

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

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