Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Posix-esque support routines for PhysicsFS.
  3.  *
  4.  * Please see the file LICENSE.txt in the source's root directory.
  5.  *
  6.  *  This file written by Ryan C. Gordon.
  7.  */
  8.  
  9. /* !!! FIXME: check for EINTR? */
  10.  
  11. #define __PHYSICSFS_INTERNAL__
  12. #include "physfs_platforms.h"
  13.  
  14. #ifdef PHYSFS_PLATFORM_POSIX
  15.  
  16. #include <unistd.h>
  17. #include <ctype.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <pwd.h>
  21. #include <dirent.h>
  22. #include <errno.h>
  23. #include <fcntl.h>
  24. #include <pthread.h>
  25.  
  26. #include "physfs_internal.h"
  27.  
  28.  
  29. static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
  30. {
  31.     switch (err)
  32.     {
  33.         case 0: return PHYSFS_ERR_OK;
  34.         case EACCES: return PHYSFS_ERR_PERMISSION;
  35.         case EPERM: return PHYSFS_ERR_PERMISSION;
  36.         case EDQUOT: return PHYSFS_ERR_NO_SPACE;
  37.         case EIO: return PHYSFS_ERR_IO;
  38.         case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
  39.         case EMLINK: return PHYSFS_ERR_NO_SPACE;
  40.         case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
  41.         case ENOENT: return PHYSFS_ERR_NOT_FOUND;
  42.         case ENOSPC: return PHYSFS_ERR_NO_SPACE;
  43.         case ENOTDIR: return PHYSFS_ERR_NOT_FOUND;
  44.         case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
  45.         case EROFS: return PHYSFS_ERR_READ_ONLY;
  46.         case ETXTBSY: return PHYSFS_ERR_BUSY;
  47.         case EBUSY: return PHYSFS_ERR_BUSY;
  48.         case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
  49.         case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
  50.         default: return PHYSFS_ERR_OS_ERROR;
  51.     } /* switch */
  52. } /* errcodeFromErrnoError */
  53.  
  54.  
  55. static inline PHYSFS_ErrorCode errcodeFromErrno(void)
  56. {
  57.     return errcodeFromErrnoError(errno);
  58. } /* errcodeFromErrno */
  59.  
  60.  
  61. static char *getUserDirByUID(void)
  62. {
  63.     uid_t uid = getuid();
  64.     struct passwd *pw;
  65.     char *retval = NULL;
  66.  
  67.     pw = getpwuid(uid);
  68.     if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0'))
  69.     {
  70.         const size_t dlen = strlen(pw->pw_dir);
  71.         const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0;
  72.         retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep);
  73.         if (retval != NULL)
  74.         {
  75.             strcpy(retval, pw->pw_dir);
  76.             if (add_dirsep)
  77.             {
  78.                 retval[dlen] = '/';
  79.                 retval[dlen+1] = '\0';
  80.             } /* if */
  81.         } /* if */
  82.     } /* if */
  83.    
  84.     return retval;
  85. } /* getUserDirByUID */
  86.  
  87.  
  88. char *__PHYSFS_platformCalcUserDir(void)
  89. {
  90.     char *retval = NULL;
  91.     char *envr = getenv("HOME");
  92.  
  93.     /* if the environment variable was set, make sure it's really a dir. */
  94.     if (envr != NULL)
  95.     {
  96.         struct stat statbuf;
  97.         if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode)))
  98.         {
  99.             const size_t envrlen = strlen(envr);
  100.             const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0;
  101.             retval = allocator.Malloc(envrlen + 1 + add_dirsep);
  102.             if (retval)
  103.             {
  104.                 strcpy(retval, envr);
  105.                 if (add_dirsep)
  106.                 {
  107.                     retval[envrlen] = '/';
  108.                     retval[envrlen+1] = '\0';
  109.                 } /* if */
  110.             } /* if */
  111.         } /* if */
  112.     } /* if */
  113.  
  114.     if (retval == NULL)
  115.         retval = getUserDirByUID();
  116.  
  117.     return retval;
  118. } /* __PHYSFS_platformCalcUserDir */
  119.  
  120.  
  121. PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
  122.                                PHYSFS_EnumerateCallback callback,
  123.                                const char *origdir, void *callbackdata)
  124. {
  125.     DIR *dir;
  126.     struct dirent *ent;
  127.     PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
  128.  
  129.     dir = opendir(dirname);
  130.     BAIL_IF(dir == NULL, errcodeFromErrno(), PHYSFS_ENUM_ERROR);
  131.  
  132.     while ((retval == PHYSFS_ENUM_OK) && ((ent = readdir(dir)) != NULL))
  133.     {
  134.         const char *name = ent->d_name;
  135.         if (name[0] == '.')  /* ignore "." and ".." */
  136.         {
  137.             if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0')))
  138.                 continue;
  139.         } /* if */
  140.  
  141.         retval = callback(callbackdata, origdir, name);
  142.         if (retval == PHYSFS_ENUM_ERROR)
  143.             PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
  144.     } /* while */
  145.  
  146.     closedir(dir);
  147.  
  148.     return retval;
  149. } /* __PHYSFS_platformEnumerate */
  150.  
  151.  
  152. int __PHYSFS_platformMkDir(const char *path)
  153. {
  154.     const int rc = mkdir(path, S_IRWXU);
  155.     BAIL_IF(rc == -1, errcodeFromErrno(), 0);
  156.     return 1;
  157. } /* __PHYSFS_platformMkDir */
  158.  
  159.  
  160. static void *doOpen(const char *filename, int mode)
  161. {
  162.     const int appending = (mode & O_APPEND);
  163.     int fd;
  164.     int *retval;
  165.     errno = 0;
  166.  
  167.     /* O_APPEND doesn't actually behave as we'd like. */
  168.     mode &= ~O_APPEND;
  169.  
  170.     fd = open(filename, mode, S_IRUSR | S_IWUSR);
  171.     BAIL_IF(fd < 0, errcodeFromErrno(), NULL);
  172.  
  173.     if (appending)
  174.     {
  175.         if (lseek(fd, 0, SEEK_END) < 0)
  176.         {
  177.             const int err = errno;
  178.             close(fd);
  179.             BAIL(errcodeFromErrnoError(err), NULL);
  180.         } /* if */
  181.     } /* if */
  182.  
  183.     retval = (int *) allocator.Malloc(sizeof (int));
  184.     if (!retval)
  185.     {
  186.         close(fd);
  187.         BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  188.     } /* if */
  189.  
  190.     *retval = fd;
  191.     return ((void *) retval);
  192. } /* doOpen */
  193.  
  194.  
  195. void *__PHYSFS_platformOpenRead(const char *filename)
  196. {
  197.     return doOpen(filename, O_RDONLY);
  198. } /* __PHYSFS_platformOpenRead */
  199.  
  200.  
  201. void *__PHYSFS_platformOpenWrite(const char *filename)
  202. {
  203.     return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
  204. } /* __PHYSFS_platformOpenWrite */
  205.  
  206.  
  207. void *__PHYSFS_platformOpenAppend(const char *filename)
  208. {
  209.     return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
  210. } /* __PHYSFS_platformOpenAppend */
  211.  
  212.  
  213. PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
  214.                                     PHYSFS_uint64 len)
  215. {
  216.     const int fd = *((int *) opaque);
  217.     ssize_t rc = 0;
  218.  
  219.     if (!__PHYSFS_ui64FitsAddressSpace(len))
  220.         BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
  221.  
  222.     rc = read(fd, buffer, (size_t) len);
  223.     BAIL_IF(rc == -1, errcodeFromErrno(), -1);
  224.     assert(rc >= 0);
  225.     assert(rc <= len);
  226.     return (PHYSFS_sint64) rc;
  227. } /* __PHYSFS_platformRead */
  228.  
  229.  
  230. PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
  231.                                      PHYSFS_uint64 len)
  232. {
  233.     const int fd = *((int *) opaque);
  234.     ssize_t rc = 0;
  235.  
  236.     if (!__PHYSFS_ui64FitsAddressSpace(len))
  237.         BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1);
  238.  
  239.     rc = write(fd, (void *) buffer, (size_t) len);
  240.     BAIL_IF(rc == -1, errcodeFromErrno(), rc);
  241.     assert(rc >= 0);
  242.     assert(rc <= len);
  243.     return (PHYSFS_sint64) rc;
  244. } /* __PHYSFS_platformWrite */
  245.  
  246.  
  247. int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
  248. {
  249.     const int fd = *((int *) opaque);
  250.     const off_t rc = lseek(fd, (off_t) pos, SEEK_SET);
  251.     BAIL_IF(rc == -1, errcodeFromErrno(), 0);
  252.     return 1;
  253. } /* __PHYSFS_platformSeek */
  254.  
  255.  
  256. PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
  257. {
  258.     const int fd = *((int *) opaque);
  259.     PHYSFS_sint64 retval;
  260.     retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
  261.     BAIL_IF(retval == -1, errcodeFromErrno(), -1);
  262.     return retval;
  263. } /* __PHYSFS_platformTell */
  264.  
  265.  
  266. PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
  267. {
  268.     const int fd = *((int *) opaque);
  269.     struct stat statbuf;
  270.     BAIL_IF(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
  271.     return ((PHYSFS_sint64) statbuf.st_size);
  272. } /* __PHYSFS_platformFileLength */
  273.  
  274.  
  275. int __PHYSFS_platformFlush(void *opaque)
  276. {
  277.     const int fd = *((int *) opaque);
  278.     if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY)
  279.         BAIL_IF(fsync(fd) == -1, errcodeFromErrno(), 0);
  280.     return 1;
  281. } /* __PHYSFS_platformFlush */
  282.  
  283.  
  284. void __PHYSFS_platformClose(void *opaque)
  285. {
  286.     const int fd = *((int *) opaque);
  287.     (void) close(fd);  /* we don't check this. You should have used flush! */
  288.     allocator.Free(opaque);
  289. } /* __PHYSFS_platformClose */
  290.  
  291.  
  292. int __PHYSFS_platformDelete(const char *path)
  293. {
  294.     BAIL_IF(remove(path) == -1, errcodeFromErrno(), 0);
  295.     return 1;
  296. } /* __PHYSFS_platformDelete */
  297.  
  298.  
  299. int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow)
  300. {
  301.     struct stat statbuf;
  302.     const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf);
  303.     BAIL_IF(rc == -1, errcodeFromErrno(), 0);
  304.  
  305.     if (S_ISREG(statbuf.st_mode))
  306.     {
  307.         st->filetype = PHYSFS_FILETYPE_REGULAR;
  308.         st->filesize = statbuf.st_size;
  309.     } /* if */
  310.  
  311.     else if(S_ISDIR(statbuf.st_mode))
  312.     {
  313.         st->filetype = PHYSFS_FILETYPE_DIRECTORY;
  314.         st->filesize = 0;
  315.     } /* else if */
  316.  
  317.     else if(S_ISLNK(statbuf.st_mode))
  318.     {
  319.         st->filetype = PHYSFS_FILETYPE_SYMLINK;
  320.         st->filesize = 0;
  321.     } /* else if */
  322.  
  323.     else
  324.     {
  325.         st->filetype = PHYSFS_FILETYPE_OTHER;
  326.         st->filesize = statbuf.st_size;
  327.     } /* else */
  328.  
  329.     st->modtime = statbuf.st_mtime;
  330.     st->createtime = statbuf.st_ctime;
  331.     st->accesstime = statbuf.st_atime;
  332.  
  333.     st->readonly = (access(fname, W_OK) == -1);
  334.     return 1;
  335. } /* __PHYSFS_platformStat */
  336.  
  337.  
  338. typedef struct
  339. {
  340.     pthread_mutex_t mutex;
  341.     pthread_t owner;
  342.     PHYSFS_uint32 count;
  343. } PthreadMutex;
  344.  
  345.  
  346. void *__PHYSFS_platformGetThreadID(void)
  347. {
  348.     return ( (void *) ((size_t) pthread_self()) );
  349. } /* __PHYSFS_platformGetThreadID */
  350.  
  351.  
  352. void *__PHYSFS_platformCreateMutex(void)
  353. {
  354.     int rc;
  355.     PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
  356.     BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
  357.     rc = pthread_mutex_init(&m->mutex, NULL);
  358.     if (rc != 0)
  359.     {
  360.         allocator.Free(m);
  361.         BAIL(PHYSFS_ERR_OS_ERROR, NULL);
  362.     } /* if */
  363.  
  364.     m->count = 0;
  365.     m->owner = (pthread_t) 0xDEADBEEF;
  366.     return ((void *) m);
  367. } /* __PHYSFS_platformCreateMutex */
  368.  
  369.  
  370. void __PHYSFS_platformDestroyMutex(void *mutex)
  371. {
  372.     PthreadMutex *m = (PthreadMutex *) mutex;
  373.  
  374.     /* Destroying a locked mutex is a bug, but we'll try to be helpful. */
  375.     if ((m->owner == pthread_self()) && (m->count > 0))
  376.         pthread_mutex_unlock(&m->mutex);
  377.  
  378.     pthread_mutex_destroy(&m->mutex);
  379.     allocator.Free(m);
  380. } /* __PHYSFS_platformDestroyMutex */
  381.  
  382.  
  383. int __PHYSFS_platformGrabMutex(void *mutex)
  384. {
  385.     PthreadMutex *m = (PthreadMutex *) mutex;
  386.     pthread_t tid = pthread_self();
  387.     if (m->owner != tid)
  388.     {
  389.         if (pthread_mutex_lock(&m->mutex) != 0)
  390.             return 0;
  391.         m->owner = tid;
  392.     } /* if */
  393.  
  394.     m->count++;
  395.     return 1;
  396. } /* __PHYSFS_platformGrabMutex */
  397.  
  398.  
  399. void __PHYSFS_platformReleaseMutex(void *mutex)
  400. {
  401.     PthreadMutex *m = (PthreadMutex *) mutex;
  402.     assert(m->owner == pthread_self());  /* catch programming errors. */
  403.     assert(m->count > 0);  /* catch programming errors. */
  404.     if (m->owner == pthread_self())
  405.     {
  406.         if (--m->count == 0)
  407.         {
  408.             m->owner = (pthread_t) 0xDEADBEEF;
  409.             pthread_mutex_unlock(&m->mutex);
  410.         } /* if */
  411.     } /* if */
  412. } /* __PHYSFS_platformReleaseMutex */
  413.  
  414. #endif  /* PHYSFS_PLATFORM_POSIX */
  415.  
  416. /* end of physfs_platform_posix.c ... */
  417.  
  418.