source: trunk/libtransmission/platform.c @ 13754

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

(libT) #3833: 0001-In-OS-X-disk-quota-s-info-already-in-bytes.patch -- patch by taem

  • 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 13754 2013-01-04 02:13:10Z 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;
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#ifdef SYS_DARWIN
867      return (freespace < 0) ? 0 : freespace;
868#else
869      return (freespace < 0) ? 0 : freespace * 1024;
870#endif
871    }
872
873  /* something went wrong */
874  return -1;
875}
876
877#ifdef HAVE_XQM
878static int64_t
879getxfsquota (char * device)
880{
881  int64_t limit;
882  int64_t freespace;
883  struct fs_disk_quota dq;
884
885  if (quotactl(QCMD(Q_XGETQUOTA, USRQUOTA), device, getuid(), (caddr_t) &dq) == 0)
886    {
887      if (dq.d_blk_softlimit > 0)
888        {
889          /* Use soft limit first */
890          limit = dq.d_blk_softlimit >> 1;
891        }
892      else if (dq.d_blk_hardlimit > 0)
893        {
894          limit = dq.d_blk_hardlimit >> 1;
895        }
896      else
897        {
898          /* No quota enabled for this user */
899          return -1;
900        }
901
902      freespace = limit - (dq.d_bcount >> 1);
903      return (freespace < 0) ? 0 : freespace * 1024;
904    }
905
906  /* something went wrong */
907  return -1;
908}
909#endif /* HAVE_XQM */
910#endif /* WIN32 */
911
912static int64_t
913tr_getQuotaFreeSpace (const char * path, char * device, char * fstype)
914{
915  int64_t ret=-1;
916
917#ifndef WIN32
918
919  /* save device for future use */
920  if (!*device)
921    {
922      char * d = getblkdev (path);
923      if (d == NULL)
924        return ret;
925      tr_strlcpy (device, d, PATH_MAX + 1);
926    }
927
928  /* save FS type for future use */
929  if (!*fstype)
930    {
931      char * fs = getfstype (device);
932      if (fs != NULL)
933        tr_strlcpy (fstype, fs, PATH_MAX + 1);
934    }
935
936  if (strcasecmp(fstype, "xfs") == 0)
937    {
938#ifdef HAVE_XQM
939      ret = getxfsquota(device);
940#endif
941    }
942  else
943    {
944      ret = getquota(device);
945    }
946
947#endif /* WIN32 */
948  return ret;
949}
950
951static int64_t
952tr_getDiskFreeSpace (const char * path)
953{
954#ifdef WIN32
955
956  uint64_t freeBytesAvailable = 0;
957  return GetDiskFreeSpaceEx (path, &freeBytesAvailable, NULL, NULL)
958    ? (int64_t)freeBytesAvailable
959    : -1;
960
961#elif defined(HAVE_STATVFS)
962
963  struct statvfs buf;
964  return statvfs(path, &buf) ? -1 : (int64_t)buf.f_bavail * (int64_t)buf.f_frsize;
965
966#else
967
968  #warning FIXME: not implemented
969  return -1;
970
971#endif
972}
973
974int64_t
975tr_getFreeSpace (const char * path, char * device, char * fstype)
976{
977  int64_t i = tr_getQuotaFreeSpace (path, device, fstype);
978
979  if (i < 0)
980    i = tr_getDiskFreeSpace (path);
981
982  return i;
983}
984
985/***
986****
987***/
988
989#ifdef WIN32
990
991/* The following mmap functions are by Joerg Walter, and were taken from
992 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm */
993
994#if defined (_MSC_VER)
995__declspec (align (4)) static LONG volatile g_sl;
996#else
997static LONG volatile g_sl __attribute__((aligned (4)));
998#endif
999
1000/* Wait for spin lock */
1001static int
1002slwait (LONG volatile *sl)
1003{
1004  while (InterlockedCompareExchange (sl, 1, 0) != 0)
1005    Sleep (0);
1006
1007  return 0;
1008}
1009
1010/* Release spin lock */
1011static int
1012slrelease (LONG volatile *sl)
1013{
1014  InterlockedExchange (sl, 0);
1015  return 0;
1016}
1017
1018/* getpagesize for windows */
1019static long
1020getpagesize (void)
1021{
1022  static long g_pagesize = 0;
1023
1024  if (!g_pagesize)
1025    {
1026      SYSTEM_INFO system_info;
1027      GetSystemInfo (&system_info);
1028      g_pagesize = system_info.dwPageSize;
1029    }
1030
1031  return g_pagesize;
1032}
1033
1034static long
1035getregionsize (void)
1036{
1037  static long g_regionsize = 0;
1038
1039  if (!g_regionsize)
1040    {
1041      SYSTEM_INFO system_info;
1042      GetSystemInfo (&system_info);
1043      g_regionsize = system_info.dwAllocationGranularity;
1044    }
1045
1046  return g_regionsize;
1047}
1048
1049void *
1050mmap (void *ptr, long  size, long  prot, long  type, long  handle, long  arg)
1051{
1052  static long g_pagesize;
1053  static long g_regionsize;
1054
1055  /* Wait for spin lock */
1056  slwait (&g_sl);
1057
1058  /* First time initialization */
1059  if (!g_pagesize)
1060    g_pagesize = getpagesize ();
1061  if (!g_regionsize)
1062    g_regionsize = getregionsize ();
1063
1064  /* Allocate this */
1065  ptr = VirtualAlloc (ptr, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
1066  if (!ptr)
1067    {
1068      ptr = (void *) -1;
1069      goto mmap_exit;
1070    }
1071
1072mmap_exit:
1073  /* Release spin lock */
1074  slrelease (&g_sl);
1075  return ptr;
1076}
1077
1078long
1079munmap (void *ptr, long size)
1080{
1081  static long g_pagesize;
1082  static long g_regionsize;
1083  int rc = -1;
1084
1085  /* Wait for spin lock */
1086  slwait (&g_sl);
1087
1088  /* First time initialization */
1089  if (!g_pagesize)
1090    g_pagesize = getpagesize ();
1091  if (!g_regionsize)
1092    g_regionsize = getregionsize ();
1093
1094  /* Free this */
1095  if (!VirtualFree (ptr, 0, MEM_RELEASE))
1096    goto munmap_exit;
1097
1098  rc = 0;
1099
1100munmap_exit:
1101  /* Release spin lock */
1102  slrelease (&g_sl);
1103  return rc;
1104}
1105
1106#endif
Note: See TracBrowser for help on using the repository browser.