diff options
| -rw-r--r-- | Changes | 1 | ||||
| -rw-r--r-- | eio.c | 208 | ||||
| -rw-r--r-- | eio.h | 68 | 
3 files changed, 240 insertions, 37 deletions
| @@ -3,6 +3,7 @@ Revision history for libeio  TODO: maybe add mincore support? available on at leats darwin, solaris, linux, freebsd  1.0 +        - readdir: corretcly set errno to ENOMEM when OOM.  	- added EIO_STACKSIZE.  	- added msync, mtouch support (untested).          - added sync_file_range (untested). @@ -1,7 +1,7 @@  /*   * libeio implementation   * - * Copyright (c) 2007,2008 Marc Alexander Lehmann <libeio@schmorp.de> + * Copyright (c) 2007,2008,2009 Marc Alexander Lehmann <libeio@schmorp.de>   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without modifica- @@ -70,7 +70,6 @@  #ifdef _WIN32    /*doh*/ -  #else  # include "config.h" @@ -81,6 +80,18 @@  # include <signal.h>  # include <dirent.h> +/* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ +# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#  define D_INO(de) (de)->d_fileno +#  define _DIRENT_HAVE_D_TYPE /* sigh */ +# elif defined(__linux) || defined(d_ino) || _XOPEN_SOURCE >= 600 +#  define D_INO(de) (de)->d_ino +# endif + +# ifdef _DIRENT_HAVE_D_TYPE +#  define D_TYPE(de) (de)->d_type +# endif +  # ifndef EIO_STRUCT_DIRENT  #  define EIO_STRUCT_DIRENT struct dirent  # endif @@ -102,6 +113,14 @@  # endif  #endif +#ifndef D_TYPE +# define D_TYPE(de) 0 +#endif + +#ifndef D_INO +# define D_INO(de) 0 +#endif +  /* number of seconds after which an idle threads exit */  #define IDLE_TIMEOUT 10 @@ -963,32 +982,98 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self)    return res;  } +static int +eio_dent_cmp (const void *a_, const void *b_) +{ +  const eio_dirent *a = (const eio_dirent *)a_; +  const eio_dirent *b = (const eio_dirent *)b_; + +  return (int)b->score - (int)a->score ? (int)b->score - (int)a->score +         : a->inode < b->inode ? -1 : a->inode > b->inode ? 1 : 0; /* int might be < ino_t */ +} +  /* read a full directory */  static void  eio__scandir (eio_req *req, etp_worker *self)  {    DIR *dirp;    EIO_STRUCT_DIRENT *entp; -  char *name, *names; -  int memlen = 4096; -  int memofs = 0; -  int res = 0; +  unsigned char *name, *names; +  int namesalloc = 4096; +  int namesoffs = 0; +  int flags = req->int1; +  eio_dirent *dents = 0; +  int dentalloc = 128; +  int dentoffs = 0; + +  req->result = -1; + +  if (!(flags & EIO_READDIR_DENTS)) +    flags &= ~(EIO_READDIR_DIRS_FIRST | EIO_READDIR_STAT_ORDER);    X_LOCK (wrklock); +    /* the corresponding closedir is in ETP_WORKER_CLEAR */    self->dirp = dirp = opendir (req->ptr1); -  req->flags |= EIO_FLAG_PTR2_FREE; -  req->ptr2 = names = malloc (memlen); +  req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE; +  req->ptr1 = names = malloc (namesalloc); +  req->ptr2 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0; +    X_UNLOCK (wrklock); -  if (dirp && names) +  if (dirp && names && (!flags || dents))      for (;;)        {          errno = 0;          entp = readdir (dirp);          if (!entp) -          break; +          { +            if (errno) +              break; + +            /* sort etc. */ +            req->int1   = flags; +            req->result = dentoffs; + +            if (flags & EIO_READDIR_STAT_ORDER || !(~flags & (EIO_READDIR_DIRS_FIRST | EIO_READDIR_FOUND_UNKNOWN)) +              { +                /* pray your qsort doesn't use quicksort */ +                qsort (dents, dentoffs, sizeof (*dents), eio_dent_cmp); /* score depends of DIRS_FIRST */ +              } +            else if (flags & EIO_READDIR_DIRS_FIRST && !(flags & EIO_READDIR_FOUND_UNKNOWN)) +              { +                /* in this case, all is known, and we just put dirs first and sort them */ +                eio_dirent *ent = dents + dentoffs; +                eio_dirent *dir = dents; + +                while (ent > dir) +                  { +                    if (dir->type == DT_DIR) +                      ++dir; +                    else  +                      { +                        --ent; + +                        if (ent->type == DT_DIR) +                          { +                            eio_dirent tmp = *dir; +                            *dir = *ent; +                            *ent = tmp; + +                            ++dir; +                          } +                      } +                  } + +                /* now sort the dirs only */ +                qsort (dents, dir - dents, sizeof (*dents), eio_dent_cmp); +              } + +              {int i; for(i=0;i<dentoffs;++i){eio_dirent *e=dents+i; printf ("%9ld %3d %s\n", e->inode,e->score,e->name);}}//D + +            break; +          }          name = entp->d_name; @@ -996,28 +1081,105 @@ eio__scandir (eio_req *req, etp_worker *self)            {              int len = strlen (name) + 1; -            res++; - -            while (memofs + len > memlen) +            while (expect_false (namesoffs + len > namesalloc))                { -                memlen *= 2; +                namesalloc *= 2;                  X_LOCK (wrklock); -                req->ptr2 = names = realloc (names, memlen); +                req->ptr1 = names = realloc (names, namesalloc);                  X_UNLOCK (wrklock);                  if (!names)                    break;                } -            memcpy (names + memofs, name, len); -            memofs += len; +            memcpy (names + namesoffs, name, len); + +            if (dents) +              { +                struct eio_dirent *ent; + +                if (expect_false (dentoffs == dentalloc)) +                  { +                    dentalloc *= 2; +                    X_LOCK (wrklock); +                    req->ptr2 = dents = realloc (dents, dentalloc * sizeof (eio_dirent)); +                    X_UNLOCK (wrklock); + +                    if (!dents) +                      break; +                  } + +                ent = dents + dentoffs; + +                ent->name    = names + namesoffs; +                ent->namelen = len - 1; +                ent->inode   = D_INO (entp); + +                switch (D_TYPE (entp)) +                  { +                    default: +                      ent->type = EIO_DT_UNKNOWN; +                      flags |= EIO_READDIR_FOUND_UNKNOWN; +                      break; + +                    #ifdef DT_FIFO +                      case DT_FIFO: ent->type = EIO_DT_FIFO; break; +                    #endif +                    #ifdef DT_CHR +                      case DT_CHR:  ent->type = EIO_DT_CHR;  break; +                    #endif           +                    #ifdef DT_DIR    +                      case DT_DIR:  ent->type = EIO_DT_DIR;  break; +                    #endif           +                    #ifdef DT_BLK    +                      case DT_BLK:  ent->type = EIO_DT_BLK;  break; +                    #endif           +                    #ifdef DT_REG    +                      case DT_REG:  ent->type = EIO_DT_REG;  break; +                    #endif           +                    #ifdef DT_LNK    +                      case DT_LNK:  ent->type = EIO_DT_LNK;  break; +                    #endif +                    #ifdef DT_SOCK +                      case DT_SOCK: ent->type = EIO_DT_SOCK; break; +                    #endif +                    #ifdef DT_WHT +                      case DT_WHT:  ent->type = EIO_DT_WHT;  break; +                    #endif +                  } + +                ent->score = 0; + +                if (flags & EIO_READDIR_DIRS_FIRST) +                  { +                    if (ent->type == EIO_DT_UNKNOWN) +                      { +                        if (*name == '.') /* leading dots are likely directories, and, in any case, rare */ +                          ent->score = 98; +                        else if (!strchr (name, '.')) /* absense of dots indicate likely dirs */ +                          ent->score = len <= 4 ? 5 : len <= 7 ? 4 : 1; /* shorter == more likely dir, but avoid too many classes */ +                      } +                    else if (ent->type == DT_DIR) +                      ent->score = 100; +                  } +              } + +            namesoffs += len; +            ++dentoffs;            }        } +  else +    req->result = -1; -  if (errno) -    res = -1; -   -  req->result = res; +  /* if user doesn't want the dents, do not provide it */ +  if (!(flags & EIO_READDIR_DENTS)) +    { +      X_LOCK (wrklock); +      free (dents); +      req->ptr2 = req->ptr1; +      req->ptr1 = 0; +      X_UNLOCK (wrklock); +    }  }  #if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) @@ -1447,9 +1609,9 @@ eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data)    return eio__1path (EIO_RMDIR, path, pri, cb, data);  } -eio_req *eio_readdir (const char *path, int pri, eio_cb cb, void *data) +eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data)  { -  return eio__1path (EIO_READDIR, path, pri, cb, data); +  REQ (EIO_READDIR); PATH; req->int1 = flags; SEND;  }  eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) @@ -1,7 +1,7 @@  /*   * libeio API header   * - * Copyright (c) 2007,2008 Marc Alexander Lehmann <libeio@schmorp.de> + * Copyright (c) 2007,2008,2009 Marc Alexander Lehmann <libeio@schmorp.de>   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without modifica- @@ -47,7 +47,8 @@ extern "C" {  #include <stddef.h>  #include <sys/types.h> -typedef struct eio_req eio_req; +typedef struct eio_req    eio_req; +typedef struct eio_dirent eio_dirent;  typedef int (*eio_cb)(eio_req *req); @@ -59,6 +60,54 @@ typedef int (*eio_cb)(eio_req *req);  # define EIO_STRUCT_STAT struct stat  #endif +/* for readdir */ + +/* eio_readdir flags */ +enum { +  EIO_READDIR_DENTS         = 0x01, /* ptr2 contains eio_dirents, not just the (unsorted) names */ +  EIO_READDIR_DIRS_FIRST    = 0x02, /* dirents gets sorted into a good stat() ing order to find directories first */ +  EIO_READDIR_STAT_ORDER    = 0x04, /* dirents gets sorted into a good stat() ing order to quickly stat all files */ +  EIO_READDIR_FOUND_UNKNOWN = 0x80, /* set by eio_readdir when *_ARRAY was set and any TYPE=UNKNOWN's were found */ + +  EIO_READDIR_CUSTOM1       = 0x100, /* for use by apps */ +  EIO_READDIR_CUSTOM2       = 0x200  /* for use by apps */ +}; + +/* using "typical" values in the hope that the compiler will do something sensible */ +enum eio_dtype { +  EIO_DT_UNKNOWN =  0, +  EIO_DT_FIFO    =  1, +  EIO_DT_CHR     =  2, +  EIO_DT_DIR     =  4, +  EIO_DT_BLK     =  6, +  EIO_DT_REG     =  8, +  EIO_DT_LNK     = 10, +  EIO_DT_SOCK    = 12, +  EIO_DT_WHT     = 14, +  EIO_DT_MAX     = 15 /* highest DT_VALUE ever, hopefully */ +}; + +struct eio_dirent { +  char *name; +  ino_t inode; +  unsigned short namelen; +  unsigned char type; +  unsigned char score; /* internal use */ +  /* 0-4 bytes padding */ +}; + +/* eio_sync_file_range flags */ + +enum { +  EIO_SYNC_FILE_RANGE_WAIT_BEFORE = 1, +  EIO_SYNC_FILE_RANGE_WRITE       = 2, +  EIO_SYNC_FILE_RANGE_WAIT_AFTER  = 4 +}; + +typedef double eio_tstamp; /* feel free to use double in your code directly */ + +/* the eio request structure */ +  enum {    EIO_CUSTOM,    EIO_OPEN, EIO_CLOSE, EIO_DUP2, @@ -78,18 +127,9 @@ enum {    EIO_BUSY  }; -/* eio_sync_file_range flags */ - -enum { -  EIO_SYNC_FILE_RANGE_WAIT_BEFORE = 1, -  EIO_SYNC_FILE_RANGE_WRITE       = 2, -  EIO_SYNC_FILE_RANGE_WAIT_AFTER  = 4 -}; - -typedef double eio_tstamp; /* feel free to use double in your code directly */ -  /* eio request structure */  /* this structure is mostly read-only */ +/* when initialising it, all members must be zero-initialised */  struct eio_req  {    eio_req volatile *next; /* private ETP */ @@ -103,7 +143,7 @@ struct eio_req    eio_tstamp nv2;  /* utime, futime: mtime */    int type;        /* EIO_xxx constant ETP */ -  int int1;        /* all applicable requests: file descriptor; sendfile: output fd; open, msync: flags */ +  int int1;        /* all applicable requests: file descriptor; sendfile: output fd; open, msync, readdir: flags */    long int2;       /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range: flags */    long int3;       /* chown, fchown: gid; mknod: dev_t */    int errorno;     /* errno value on syscall return */ @@ -192,7 +232,7 @@ eio_req *eio_truncate  (const char *path, off_t offset, int pri, eio_cb cb, void  eio_req *eio_chown     (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data);  eio_req *eio_chmod     (const char *path, mode_t mode, int pri, eio_cb cb, void *data);  eio_req *eio_mkdir     (const char *path, mode_t mode, int pri, eio_cb cb, void *data); -eio_req *eio_readdir   (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ +eio_req *eio_readdir   (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */  eio_req *eio_rmdir     (const char *path, int pri, eio_cb cb, void *data);  eio_req *eio_unlink    (const char *path, int pri, eio_cb cb, void *data);  eio_req *eio_readlink  (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ | 
