summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes1
-rw-r--r--eio.c208
-rw-r--r--eio.h68
3 files changed, 240 insertions, 37 deletions
diff --git a/Changes b/Changes
index d4c3a12..a8d690d 100644
--- a/Changes
+++ b/Changes
@@ -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).
diff --git a/eio.c b/eio.c
index 9fcb42c..ddcd296 100644
--- a/eio.c
+++ b/eio.c
@@ -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)
diff --git a/eio.h b/eio.h
index 12fc3ed..2ef2a55 100644
--- a/eio.h
+++ b/eio.h
@@ -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 */