source: trunk/libtransmission/platform.c @ 13868

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

make all the log functions/structs/enums use a single 'tr_log' namespace, such as tr_logGetQueue, tr_logAddInfo, tr_logIsLevelActive

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