diff options
author | root <root> | 2009-06-06 17:25:13 +0000 |
---|---|---|
committer | root <root> | 2009-06-06 17:25:13 +0000 |
commit | 76f5f8782e61db7dbdcb127001ee753ab6743afc (patch) | |
tree | 5a5d8f63bba6612399f74dbc15aac0f11a01a225 | |
parent | d64cc727dc4e875228983078ebca990e8d6b9dbd (diff) |
*** empty log message ***
-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 */ |