source: trunk/libtransmission/platform.c @ 13975

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

(libT) #5275 'quotactl breaks compilation of libT/platform.c on Solaris' -- fixed with patch from spaam

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