source: trunk/libtransmission/platform.c @ 14080

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

whitespace/indentation

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