rktio: switch file & directory operations to rktio

This commit is contained in:
Matthew Flatt 2017-06-13 18:37:18 -06:00
parent 425fe36fa5
commit 6268cd7ce9
13 changed files with 690 additions and 2058 deletions

View File

@ -304,9 +304,11 @@ SCONFIG = $(srcdir)/../sconfig.h $(srcdir)/../uconfig.h ../mzconfig.h
COMMON_HEADERS = $(srcdir)/schpriv.h $(srcdir)/schexn.h $(SCONFIG) $(srcdir)/../include/scheme.h \ COMMON_HEADERS = $(srcdir)/schpriv.h $(srcdir)/schexn.h $(SCONFIG) $(srcdir)/../include/scheme.h \
$(srcdir)/../include/schthread.h $(srcdir)/mzrt.h $(srcdir)/mzrt_cas.inc \ $(srcdir)/../include/schthread.h $(srcdir)/mzrt.h $(srcdir)/mzrt_cas.inc \
$(srcdir)/longdouble/longdouble.h $(srcdir)/../utils/schiptr.h \ $(srcdir)/longdouble/longdouble.h $(srcdir)/../utils/schiptr.h
RKTIO_HEADERS = $(srcdir)/schrktio.h \
$(srcdir)/../../rktio/rktio.h $(srcdir)/../../rktio/rktio_platform.h \ $(srcdir)/../../rktio/rktio.h $(srcdir)/../../rktio/rktio_platform.h \
../../rktio/rktio_config.h ../../rktio/rktio_config.h
JIT_HEADERS = $(srcdir)/jit.h $(srcdir)/jitfpu.h $(srcdir)/stypes.h \ JIT_HEADERS = $(srcdir)/jit.h $(srcdir)/jitfpu.h $(srcdir)/stypes.h \
$(srcdir)/lightning/i386/core.h $(srcdir)/lightning/i386/core-common.h \ $(srcdir)/lightning/i386/core.h $(srcdir)/lightning/i386/core-common.h \
$(srcdir)/lightning/i386/asm.h $(srcdir)/lightning/i386/asm-common.h \ $(srcdir)/lightning/i386/asm.h $(srcdir)/lightning/i386/asm-common.h \
@ -324,7 +326,7 @@ JIT_HEADERS = $(srcdir)/jit.h $(srcdir)/jitfpu.h $(srcdir)/stypes.h \
$(srcdir)/lightning/arm/fp-vfp.h $(srcdir)/lightning/arm/fp-swf.h \ $(srcdir)/lightning/arm/fp-vfp.h $(srcdir)/lightning/arm/fp-swf.h \
$(srcdir)/future.h $(srcdir)/jit_ts.c $(srcdir)/jit_ts_protos.h $(srcdir)/future.h $(srcdir)/jit_ts.c $(srcdir)/jit_ts_protos.h
salloc.@LTO@: $(COMMON_HEADERS) \ salloc.@LTO@: $(COMMON_HEADERS) $(RKTIOHEADERS) \
$(srcdir)/../gc/gc.h $(srcdir)/mzmark_salloc.inc $(srcdir)/../gc/gc.h $(srcdir)/mzmark_salloc.inc
bignum.@LTO@: $(COMMON_HEADERS) \ bignum.@LTO@: $(COMMON_HEADERS) \
$(srcdir)/stypes.h $(srcdir)/stypes.h
@ -351,9 +353,9 @@ eval.@LTO@: $(COMMON_HEADERS) \
$(srcdir)/stypes.h $(srcdir)/mzmark_eval.inc \ $(srcdir)/stypes.h $(srcdir)/mzmark_eval.inc \
$(srcdir)/schmach.h $(srcdir)/mzstkchk.h $(srcdir)/schrunst.h \ $(srcdir)/schmach.h $(srcdir)/mzstkchk.h $(srcdir)/schrunst.h \
$(srcdir)/future.h $(srcdir)/future.h
file.@LTO@: $(COMMON_HEADERS) \ file.@LTO@: $(COMMON_HEADERS) $(RKTIOHEADERS) \
$(srcdir)/stypes.h $(srcdir)/stypes.h
fun.@LTO@: $(COMMON_HEADERS) \ fun.@LTO@: $(COMMON_HEADERS) $(RKTIOHEADERS) \
$(srcdir)/stypes.h $(srcdir)/mzmark_fun.inc $(srcdir)/schmap.inc \ $(srcdir)/stypes.h $(srcdir)/mzmark_fun.inc $(srcdir)/schmap.inc \
$(srcdir)/future.h $(srcdir)/future.h
future.@LTO@: $(COMMON_HEADERS) $(srcdir)/future.h $(SCONFIG) \ future.@LTO@: $(COMMON_HEADERS) $(srcdir)/future.h $(SCONFIG) \

View File

@ -31,6 +31,7 @@
#include "schminc.h" #include "schminc.h"
#include "schmach.h" #include "schmach.h"
#include "schexpobs.h" #include "schexpobs.h"
#include "schrktio.h"
#ifdef MZ_USE_FUTURES #ifdef MZ_USE_FUTURES
# include "future.h" # include "future.h"
#endif #endif

View File

@ -24,6 +24,7 @@
*/ */
#include "schpriv.h" #include "schpriv.h"
#include "schrktio.h"
#include <ctype.h> #include <ctype.h>
#ifdef DOS_FILE_SYSTEM #ifdef DOS_FILE_SYSTEM
# include <windows.h> # include <windows.h>
@ -281,6 +282,7 @@ Scheme_Config *scheme_init_error_escape_proc(Scheme_Config *config)
%- = skip int %- = skip int
%L = line number as intptr_t, -1 means no line %L = line number as intptr_t, -1 means no line
%R = get error numebr and string from rktio
%e = error number for strerror() %e = error number for strerror()
%E = error number for platform-specific error string %E = error number for platform-specific error string
%Z = potential platform-specific error number; additional char* %Z = potential platform-specific error number; additional char*
@ -480,6 +482,44 @@ static intptr_t sch_vsprintf(char *s, intptr_t maxlen, const char *msg, va_list
} }
} }
break; break;
case 'R':
{
intptr_t errid;
intptr_t errkind;
const char *es;
intptr_t elen;
errkind = rktio_get_last_error(scheme_rktio);
errid = rktio_get_last_error(scheme_rktio);
es = rktio_get_error_string(scheme_rktio, errkind, errid);
sprintf(buf, "; errno=%" PRIdPTR "", errid);
elen = strlen(es);
tlen = strlen(buf);
t = (const char *)scheme_malloc_atomic(tlen+elen+1);
memcpy((char *)t, es, elen);
memcpy((char *)t+elen, buf, tlen+1);
tlen += elen;
if (_errno_val) {
Scheme_Object *err_kind;
switch (errkind) {
case RKTIO_ERROR_KIND_WINDOWS:
err_kind = windows_symbol;
break;
case RKTIO_ERROR_KIND_POSIX:
err_kind = posix_symbol;
break;
case RKTIO_ERROR_KIND_GAI:
err_kind = gai_symbol;
break;
default:
err_kind = NULL;
}
if (err_kind) {
err_kind = scheme_make_pair(scheme_make_integer_value(errid), err_kind);
*_errno_val = err_kind;
}
}
}
break;
case 'e': case 'e':
case 'm': case 'm':
case 'E': case 'E':
@ -668,6 +708,7 @@ static intptr_t sch_vsprintf(char *s, intptr_t maxlen, const char *msg, va_list
} }
} }
while (tlen && i < maxlen) { while (tlen && i < maxlen) {
s[i++] = *t; s[i++] = *t;
t = t XFORM_OK_PLUS 1; t = t XFORM_OK_PLUS 1;
@ -705,6 +746,12 @@ static intptr_t scheme_sprintf(char *s, intptr_t maxlen, const char *msg, ...)
return len; return len;
} }
int scheme_last_error_is_racket(int errid)
{
return ((rktio_get_last_error_kind(scheme_rktio) == RKTIO_ERROR_KIND_RACKET)
&& (rktio_get_last_error(scheme_rktio) == errid));
}
#define ESCAPING_NONCM_PRIM(name, func, a1, a2, env) \ #define ESCAPING_NONCM_PRIM(name, func, a1, a2, env) \
p = scheme_make_noncm_prim(func, name, a1, a2); \ p = scheme_make_noncm_prim(func, name, a1, a2); \
SCHEME_PRIM_PROC_FLAGS(p) |= scheme_intern_prim_opt_flags(SCHEME_PRIM_ALWAYS_ESCAPES); \ SCHEME_PRIM_PROC_FLAGS(p) |= scheme_intern_prim_opt_flags(SCHEME_PRIM_ALWAYS_ESCAPES); \

File diff suppressed because it is too large Load Diff

View File

@ -4067,6 +4067,8 @@ Scheme_Object *scheme_prune_bindings_table(Scheme_Object *bindings, Scheme_Objec
#define NOT_SUPPORTED_STR "unsupported on this platform" #define NOT_SUPPORTED_STR "unsupported on this platform"
int scheme_last_error_is_racket(int errid);
void scheme_read_err(Scheme_Object *port, void scheme_read_err(Scheme_Object *port,
Scheme_Object *stxsrc, Scheme_Object *stxsrc,
intptr_t line, intptr_t column, intptr_t pos, intptr_t span, intptr_t line, intptr_t column, intptr_t pos, intptr_t span,

View File

@ -1,2 +1,4 @@
/* The rktio library has no callbacks, so no GC. */
#define RKTIO_EXTERN XFORM_NONGCING extern #define RKTIO_EXTERN XFORM_NONGCING extern
#include "rktio.h" #include "rktio.h"

View File

@ -8,12 +8,59 @@ The library is meant to be
* always non-blocking; * always non-blocking;
* independent of global state (except on Windows, where internal * independent of global state, so that it works with or without
global state is managed appropriately with locks, and except for threads; and
Unix process handling without pthreads), so that it works with or
without threads; and
* easily callable though a FFI. * easily callable though a FFI.
Many such libraries exist already. This one happens to have exactly Many such libraries exist already. This one happens to have exactly
the things that a Racket implementation needs. the things that a Racket implementation needs.
============================================================
Allocation conventions:
* Unless otherwise specified, returned data must be deallocated ---
using a type-specific deallocation function if provided or
rktio_free() otherwise. The rktio_free() function is the same as
free().
* There's no reference counting. If object A refers to object B, then
a client must keep object B alive as long as object A exists.
* String arguments are copied if they must be retained. Unless
otherwise specified, creating an object A with string S doesn't
require that S stay live as long as A exists. String results are
generally allocated and must be freed by the client.
Return type conventions:
* A return type `rktio_ok_t` (alias for `int`) means that 1 is
returned for succes and 0 for error. Use
rktio_get_last_error_kind() and rktio_get_last_error() for more
information about a 0 result.
* A return type `rktio_tri_t` (alias for `int`) means that 0 is
returned for an expected failuree, some `RKTIO_...` (alias for 1)
is returned for success, and `RKTIO_...ERROR` (alias for -2) is
returned for some error. Use rktio_get_last_error_kind() and
rktio_get_last_error() for more information about a
`RKTIO_...ERROR` result.
* For a pointer return type, unless otherwise specified, a NULL
result means an error. Use rktio_get_last_error_kind() and
rktio_get_last_error() for more information about the error.
* If a function returns `void`, you can rely on it to not change the
error reported by rktio_get_last_error_kind() and
rktio_get_last_error().
Thread conventions:
* A given `rktio_t` can be used from only one thread at a time.
Otherwise, as long as the initial call to rktio_init() returns
before a second call, there are no threading requirements.
Signals:
* SIGCHLD may be enabled, blocked, and/or handled.

View File

@ -8,7 +8,7 @@ static void do_check_valid(rktio_t *rktio, int ok, int where)
/* Beware that a reported error is nonsense if the failure /* Beware that a reported error is nonsense if the failure
was an unexpected result insteda of an error result. */ was an unexpected result insteda of an error result. */
if (!ok) { if (!ok) {
printf("error at %d: %d@%d = %s\n", printf(">> ERROR at %d: %d@%d = %s\n",
where, where,
rktio_get_last_error(rktio), rktio_get_last_error(rktio),
rktio_get_last_error_kind(rktio), rktio_get_last_error_kind(rktio),
@ -21,7 +21,7 @@ static void do_check_valid(rktio_t *rktio, int ok, int where)
static void do_check_expected_error(rktio_t *rktio, int err, int where) static void do_check_expected_error(rktio_t *rktio, int err, int where)
{ {
if (!err) { if (!err) {
printf("error expected at %d\n", printf(">> ERROR expected at %d\n",
where); where);
} }
} }
@ -29,11 +29,11 @@ static void do_check_expected_error(rktio_t *rktio, int err, int where)
static void do_check_expected_racket_error(rktio_t *rktio, int err, int what, int where) static void do_check_expected_racket_error(rktio_t *rktio, int err, int what, int where)
{ {
if (!err) { if (!err) {
printf("error expected at %d\n", printf(">> ERROR expected at %d\n",
where); where);
} else if ((what != rktio_get_last_error(rktio)) } else if ((what != rktio_get_last_error(rktio))
|| (RKTIO_ERROR_KIND_RACKET != rktio_get_last_error_kind(rktio))) { || (RKTIO_ERROR_KIND_RACKET != rktio_get_last_error_kind(rktio))) {
printf("wrong error at %d: %d@%d = %s\n", printf(">> WRONG ERROR at %d: %d@%d = %s\n",
where, where,
rktio_get_last_error(rktio), rktio_get_last_error(rktio),
rktio_get_last_error_kind(rktio), rktio_get_last_error_kind(rktio),
@ -534,8 +534,8 @@ int main(int argc, char **argv)
ts1 = rktio_get_file_modify_seconds(rktio, "test1"); ts1 = rktio_get_file_modify_seconds(rktio, "test1");
perms = rktio_get_file_or_directory_permissions(rktio, "test1", 0); perms = rktio_get_file_or_directory_permissions(rktio, "test1", 0);
check_valid(perms != -1); check_valid(perms != -1);
check_valid(perms & (RKTIO_PERMISSION_READ << 6)); check_valid(perms & RKTIO_PERMISSION_READ);
check_valid(perms & (RKTIO_PERMISSION_WRITE << 6)); check_valid(perms & RKTIO_PERMISSION_WRITE);
perms = rktio_get_file_or_directory_permissions(rktio, "test1", 1); perms = rktio_get_file_or_directory_permissions(rktio, "test1", 1);
check_valid(perms != -1); check_valid(perms != -1);
check_valid(perms & (RKTIO_PERMISSION_READ << 6)); check_valid(perms & (RKTIO_PERMISSION_READ << 6));
@ -584,7 +584,7 @@ int main(int argc, char **argv)
/* Listing directory content */ /* Listing directory content */
ls = rktio_directory_list_start(rktio, pwd, 0); ls = rktio_directory_list_start(rktio, pwd);
check_valid(ls); check_valid(ls);
saw_file = 0; saw_file = 0;
while (1) { while (1) {

View File

@ -11,13 +11,23 @@
Almost every rktio_...() function takes it as the first argument. */ Almost every rktio_...() function takes it as the first argument. */
typedef struct rktio_t rktio_t; typedef struct rktio_t rktio_t;
/* Call rktio_init() before anything else. The first call to
rktio_init() must return before any additional calls (in other
threads), but there's no ordering requirement after that. */
RKTIO_EXTERN rktio_t *rktio_init(void); RKTIO_EXTERN rktio_t *rktio_init(void);
/* Call rktio_destroy() as the last thing. Everything else must be
explicitly deallocated/closed/forgotten before calling
rktio_destroy(). */
RKTIO_EXTERN void rktio_destroy(rktio_t *); RKTIO_EXTERN void rktio_destroy(rktio_t *);
/* Normally equivalent to free(), but ensures the same malloc()/free() /* Normally equivalent to free(), but ensures the same malloc()/free()
that rktio function use: */ that rktio function use: */
RKTIO_EXTERN void rktio_free(void *p); RKTIO_EXTERN void rktio_free(void *p);
typedef int rktio_ok_t;
typedef int rktio_tri_t;
/*************************************************/ /*************************************************/
/* Reading and writing files */ /* Reading and writing files */
@ -50,7 +60,7 @@ RKTIO_EXTERN int rktio_fd_is_terminal(rktio_t *rktio, rktio_fd_t *rfd);
RKTIO_EXTERN int rktio_fd_modes(rktio_t *rktio, rktio_fd_t *rfd); RKTIO_EXTERN int rktio_fd_modes(rktio_t *rktio, rktio_fd_t *rfd);
RKTIO_EXTERN rktio_fd_t *rktio_open(rktio_t *rktio, char *src, int modes); RKTIO_EXTERN rktio_fd_t *rktio_open(rktio_t *rktio, char *src, int modes);
RKTIO_EXTERN int rktio_close(rktio_t *rktio, rktio_fd_t *fd); RKTIO_EXTERN rktio_ok_t rktio_close(rktio_t *rktio, rktio_fd_t *fd);
RKTIO_EXTERN rktio_fd_t *rktio_dup(rktio_t *rktio, rktio_fd_t *rfd); RKTIO_EXTERN rktio_fd_t *rktio_dup(rktio_t *rktio, rktio_fd_t *rfd);
RKTIO_EXTERN void rktio_forget(rktio_t *rktio, rktio_fd_t *fd); RKTIO_EXTERN void rktio_forget(rktio_t *rktio, rktio_fd_t *fd);
@ -58,21 +68,26 @@ RKTIO_EXTERN void rktio_forget(rktio_t *rktio, rktio_fd_t *fd);
#define RKTIO_READ_EOF (-1) #define RKTIO_READ_EOF (-1)
#define RKTIO_READ_ERROR (-2) #define RKTIO_READ_ERROR (-2)
#define RKTIO_WRITE_ERROR (-2) #define RKTIO_WRITE_ERROR (-2)
#define RKTIO_POLL_ERROR (-2)
#define RKTIO_POLL_READY 1
/* The read and write functions return the number of bytes read/write
in non-blocking mode, possibly 0. A read can produce `RKTIO_READ_EOF`
for end-of-file or `RKTIO_READ_ERROR` for an error. Similarly, write
can produce `RKTIO_WRITE_ERROR`. */
RKTIO_EXTERN intptr_t rktio_read(rktio_t *rktio, rktio_fd_t *fd, char *buffer, intptr_t len); RKTIO_EXTERN intptr_t rktio_read(rktio_t *rktio, rktio_fd_t *fd, char *buffer, intptr_t len);
RKTIO_EXTERN intptr_t rktio_write(rktio_t *rktio, rktio_fd_t *fd, char *buffer, intptr_t len); RKTIO_EXTERN intptr_t rktio_write(rktio_t *rktio, rktio_fd_t *fd, char *buffer, intptr_t len);
RKTIO_EXTERN int rktio_poll_read_ready(rktio_t *rktio, rktio_fd_t *rfd); #define RKTIO_POLL_ERROR (-2)
RKTIO_EXTERN int rktio_poll_write_ready(rktio_t *rktio, rktio_fd_t *rfd); #define RKTIO_POLL_READY 1
RKTIO_EXTERN int rktio_poll_write_flushed(rktio_t *rktio, rktio_fd_t *rfd);
RKTIO_EXTERN rktio_tri_t rktio_poll_read_ready(rktio_t *rktio, rktio_fd_t *rfd);
RKTIO_EXTERN rktio_tri_t rktio_poll_write_ready(rktio_t *rktio, rktio_fd_t *rfd);
RKTIO_EXTERN rktio_tri_t rktio_poll_write_flushed(rktio_t *rktio, rktio_fd_t *rfd);
#define RKTIO_LOCK_ERROR (-2) #define RKTIO_LOCK_ERROR (-2)
#define RKTIO_LOCK_ACQUIRED 1 #define RKTIO_LOCK_ACQUIRED 1
RKTIO_EXTERN int rktio_file_lock_try(rktio_t *rktio, rktio_fd_t *rfd, int excl); RKTIO_EXTERN rktio_tri_t rktio_file_lock_try(rktio_t *rktio, rktio_fd_t *rfd, int excl);
RKTIO_EXTERN int rktio_file_unlock(rktio_t *rktio, rktio_fd_t *rfd); RKTIO_EXTERN rktio_tri_t rktio_file_unlock(rktio_t *rktio, rktio_fd_t *rfd);
/*************************************************/ /*************************************************/
/* Network */ /* Network */
@ -297,16 +312,31 @@ RKTIO_EXTERN int rktio_directory_exists(rktio_t *rktio, char *dirname);
RKTIO_EXTERN int rktio_link_exists(rktio_t *rktio, char *filename); RKTIO_EXTERN int rktio_link_exists(rktio_t *rktio, char *filename);
RKTIO_EXTERN int rktio_is_regular_file(rktio_t *rktio, char *filename); RKTIO_EXTERN int rktio_is_regular_file(rktio_t *rktio, char *filename);
RKTIO_EXTERN int rktio_delete_file(rktio_t *rktio, char *fn, int enable_write_on_fail); RKTIO_EXTERN rktio_ok_t rktio_delete_file(rktio_t *rktio, char *fn, int enable_write_on_fail);
RKTIO_EXTERN int rktio_rename_file(rktio_t *rktio, char *dest, char *src, int exists_ok);
/* Can report `RKTIO_ERROR_EXISTS`: */
RKTIO_EXTERN rktio_ok_t rktio_rename_file(rktio_t *rktio, char *dest, char *src, int exists_ok);
RKTIO_EXTERN char *rktio_get_current_directory(rktio_t *rktio); RKTIO_EXTERN char *rktio_get_current_directory(rktio_t *rktio);
RKTIO_EXTERN int rktio_set_current_directory(rktio_t *rktio, const char *path); RKTIO_EXTERN rktio_ok_t rktio_set_current_directory(rktio_t *rktio, const char *path);
RKTIO_EXTERN int rktio_make_directory(rktio_t *rktio, char *filename);
RKTIO_EXTERN int rktio_delete_directory(rktio_t *rktio, char *filename, char *current_directory, int enable_write_on_fail);
/* Can report `RKTIO_ERROR_EXISTS`: */
RKTIO_EXTERN rktio_ok_t rktio_make_directory(rktio_t *rktio, char *filename);
/* The `current_directory` argument is used on Windows to avoid being
in `filename` (instead) as a directory while trying to delete it.
The `enable_write_on_fail` argument also applied to Windows. */
RKTIO_EXTERN rktio_ok_t rktio_delete_directory(rktio_t *rktio, char *filename, char *current_directory,
int enable_write_on_fail);
/* Argument should not have a trailing separator. Can report
`RKTIO_ERROR_NOT_A_LINK`. */
RKTIO_EXTERN char *rktio_readlink(rktio_t *rktio, char *fullfilename); RKTIO_EXTERN char *rktio_readlink(rktio_t *rktio, char *fullfilename);
RKTIO_EXTERN int rktio_make_link(rktio_t *rktio, char *src, char *dest, int dest_is_directory);
/* The `dest_is_directory` argument is used only
on Windows. Can report `RKTIO_ERROR_EXISTS`. */
RKTIO_EXTERN rktio_ok_t rktio_make_link(rktio_t *rktio, char *src, char *dest,
int dest_is_directory);
/*************************************************/ /*************************************************/
/* File attributes */ /* File attributes */
@ -317,14 +347,15 @@ typedef struct {
typedef intptr_t rktio_timestamp_t; typedef intptr_t rktio_timestamp_t;
typedef struct {
uintptr_t a, b, c;
} rktio_identity_t;
RKTIO_EXTERN rktio_size_t *rktio_file_size(rktio_t *rktio, char *filename); RKTIO_EXTERN rktio_size_t *rktio_file_size(rktio_t *rktio, char *filename);
RKTIO_EXTERN rktio_timestamp_t *rktio_get_file_modify_seconds(rktio_t *rktio, char *file); RKTIO_EXTERN rktio_timestamp_t *rktio_get_file_modify_seconds(rktio_t *rktio, char *file);
RKTIO_EXTERN int rktio_set_file_modify_seconds(rktio_t *rktio, char *file, rktio_timestamp_t secs); RKTIO_EXTERN rktio_ok_t rktio_set_file_modify_seconds(rktio_t *rktio, char *file, rktio_timestamp_t secs);
typedef struct {
uintptr_t a, b, c;
int a_bits, b_bits, c_bits; /* size of each in bits */
} rktio_identity_t;
RKTIO_EXTERN rktio_identity_t *rktio_fd_identity(rktio_t *rktio, rktio_fd_t *fd); RKTIO_EXTERN rktio_identity_t *rktio_fd_identity(rktio_t *rktio, rktio_fd_t *fd);
RKTIO_EXTERN rktio_identity_t *rktio_path_identity(rktio_t *rktio, char *path, int follow_links); RKTIO_EXTERN rktio_identity_t *rktio_path_identity(rktio_t *rktio, char *path, int follow_links);
@ -332,22 +363,41 @@ RKTIO_EXTERN rktio_identity_t *rktio_path_identity(rktio_t *rktio, char *path, i
/*************************************************/ /*************************************************/
/* Permissions */ /* Permissions */
/* Must match OS bits: */ /* Should match OS bits: */
#define RKTIO_PERMISSION_READ 0x4 #define RKTIO_PERMISSION_READ 0x4
#define RKTIO_PERMISSION_WRITE 0x2 #define RKTIO_PERMISSION_WRITE 0x2
#define RKTIO_PERMISSION_EXEC 0x1 #define RKTIO_PERMISSION_EXEC 0x1
#define RKTIO_PERMISSION_ERROR (-1)
/* Result is `RKTIO_PERMISSION_ERROR` for error, otherwise a combination of
bits. If not `all_bits`, then use constants above. */
RKTIO_EXTERN int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int all_bits); RKTIO_EXTERN int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int all_bits);
RKTIO_EXTERN int rktio_set_file_or_directory_permissions(rktio_t *rktio, char *filename, int new_bits);
/* The `new_bits` format corresponds to `all_bits` for getting permissions.
Can report `RKTIO_ERROR_BAD_PERMISSION` for bits that make no sense. */
RKTIO_EXTERN rktio_ok_t rktio_set_file_or_directory_permissions(rktio_t *rktio, char *filename, int new_bits);
/*************************************************/ /*************************************************/
/* Directory listing */ /* Directory listing */
typedef struct rktio_directory_list_t rktio_directory_list_t; typedef struct rktio_directory_list_t rktio_directory_list_t;
RKTIO_EXTERN rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename, int is_drive); /* On Windows, the given `filename` must be normalized and not have
`.` or `..`: */
RKTIO_EXTERN rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename);
/* Returns an unallocated "" and deallocates `dl` when the iteration
is complete. A NULL result would mean an error without deallocating
`dl`, but that doesn't currently happen. */
RKTIO_EXTERN char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl); RKTIO_EXTERN char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl);
/* Interrupt a directory list in progress, not needed after
rktio_directory_list_step() returns "": */
RKTIO_EXTERN void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl);
/* Returns a NULL-terminated array. Free each string. Currently never
errors. */
RKTIO_EXTERN char **rktio_filesystem_root_list(rktio_t *rktio); RKTIO_EXTERN char **rktio_filesystem_root_list(rktio_t *rktio);
/*************************************************/ /*************************************************/
@ -355,9 +405,11 @@ RKTIO_EXTERN char **rktio_filesystem_root_list(rktio_t *rktio);
typedef struct rktio_file_copy_t rktio_file_copy_t; typedef struct rktio_file_copy_t rktio_file_copy_t;
/* Can report `RKTIO_ERROR_EXISTS`: */
RKTIO_EXTERN rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, char *dest, char *src, int exists_ok); RKTIO_EXTERN rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, char *dest, char *src, int exists_ok);
RKTIO_EXTERN int rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc); RKTIO_EXTERN int rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc);
RKTIO_EXTERN int rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc); RKTIO_EXTERN rktio_ok_t rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc);
RKTIO_EXTERN void rktio_copy_file_stop(rktio_t *rktio, rktio_file_copy_t *fc); RKTIO_EXTERN void rktio_copy_file_stop(rktio_t *rktio, rktio_file_copy_t *fc);
/*************************************************/ /*************************************************/
@ -377,6 +429,10 @@ enum {
}; };
RKTIO_EXTERN char *rktio_system_path(rktio_t *rktio, int which); RKTIO_EXTERN char *rktio_system_path(rktio_t *rktio, int which);
/* Path must start with tilde, otherwise `RKTIO_ERROR_NO_TILDE`.
Other possible errors are `RKTIO_ERROR_ILL_FORMED_USER` and
`RKTIO_ERROR_UNKNOWN_USER`. */
RKTIO_EXTERN char *rktio_expand_user_tilde(rktio_t *rktio, char *filename); RKTIO_EXTERN char *rktio_expand_user_tilde(rktio_t *rktio, char *filename);
/*************************************************/ /*************************************************/
@ -423,7 +479,7 @@ enum {
/* Error IDs of kind RKTIO_ERROR_KIND_RACKET */ /* Error IDs of kind RKTIO_ERROR_KIND_RACKET */
enum { enum {
RKTIO_ERROR_UNSUPPORTED, RKTIO_ERROR_UNSUPPORTED = 1,
RKTIO_ERROR_EXISTS, RKTIO_ERROR_EXISTS,
RKTIO_ERROR_LINK_FAILED, RKTIO_ERROR_LINK_FAILED,
RKTIO_ERROR_NOT_A_LINK, RKTIO_ERROR_NOT_A_LINK,
@ -455,7 +511,8 @@ RKTIO_EXTERN int rktio_get_last_error(rktio_t *rktio);
RKTIO_EXTERN int rktio_get_last_error_kind(rktio_t *rktio); RKTIO_EXTERN int rktio_get_last_error_kind(rktio_t *rktio);
/* The returned strings for `rktio_...error_string()` should not be /* The returned strings for `rktio_...error_string()` should not be
deallocated. */ deallocated, but it only lasts reliably until the next call to
either of the functions. */
RKTIO_EXTERN const char *rktio_get_last_error_string(rktio_t *rktio); RKTIO_EXTERN const char *rktio_get_last_error_string(rktio_t *rktio);
RKTIO_EXTERN const char *rktio_get_error_string(rktio_t *rktio, int kind, int errid); RKTIO_EXTERN const char *rktio_get_error_string(rktio_t *rktio, int kind, int errid);

View File

@ -6,6 +6,35 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
typedef struct err_str_t {
int id;
char *str;
} err_str_t;
err_str_t err_strs[] = {
{ RKTIO_ERROR_UNSUPPORTED, "unsupported" },
{ RKTIO_ERROR_EXISTS, "file or directory already exists"},
{ RKTIO_ERROR_LINK_FAILED, "link creation failed" },
{ RKTIO_ERROR_NOT_A_LINK, "not a link" },
{ RKTIO_ERROR_BAD_PERMISSION, "unsupported permission value" },
{ RKTIO_ERROR_IS_A_DIRECTORY, "path refers to a directory" },
{ RKTIO_ERROR_NOT_A_DIRECTORY, "path does not refer to a directory" },
{ RKTIO_ERROR_NO_TILDE, "path does not start with a tilde" },
{ RKTIO_ERROR_ILL_FORMED_USER, "ill-formed username in path" },
{ RKTIO_ERROR_UNKNOWN_USER, "unknown username in path" },
{ RKTIO_ERROR_INIT_FAILED, "initialization failed" },
{ RKTIO_ERROR_LTPS_NOT_FOUND, "not handle found" },
{ RKTIO_ERROR_LTPS_REMOVED, "handles successfully removed" },
{ RKTIO_ERROR_CONNECT_TRYING_NEXT, "connection failed, but can try again" },
{ RKTIO_ERROR_ACCEPT_NOT_READY, "no connection ready to accept" },
{ RKTIO_ERROR_HOST_AND_PORT_BOTH_UNSPECIFIED, "neither hostname nor port number specified" },
{ RKTIO_ERROR_INFO_TRY_AGAIN, "spurious empty UDP message; try again" },
{ RKTIO_ERROR_TRY_AGAIN, "no UDP message available" },
{ RKTIO_ERROR_TRY_AGAIN_WITH_IPV4, "listen failed, but try again with just IPv4 addresses" },
{ RKTIO_ERROR_TIME_OUT_OF_RANGE, "time value out-of-range for date conversion" },
{ 0, NULL }
};
void rktio_get_posix_error(rktio_t *rktio) void rktio_get_posix_error(rktio_t *rktio)
{ {
rktio->errid = errno; rktio->errid = errno;
@ -45,12 +74,50 @@ int rktio_get_last_error_kind(rktio_t *rktio)
const char *rktio_get_error_string(rktio_t *rktio, int kind, int errid) const char *rktio_get_error_string(rktio_t *rktio, int kind, int errid)
{ {
const char *s = NULL; const char *s = NULL;
if (kind == RKTIO_ERROR_KIND_POSIX) { if (kind == RKTIO_ERROR_KIND_RACKET) {
int i;
for (i = 0; err_strs[i].str; i++) {
if (err_strs[i].id == errid)
return err_strs[i].str;
}
} else if (kind == RKTIO_ERROR_KIND_POSIX) {
#ifndef NO_STRERROR_AVAILABLE #ifndef NO_STRERROR_AVAILABLE
s = strerror(errid); s = strerror(errid);
#endif #endif
} else if (kind == RKTIO_ERROR_KIND_GAI) } else if (kind == RKTIO_ERROR_KIND_GAI)
s = rktio_gai_strerror(errid); s = rktio_gai_strerror(errid);
#ifdef RKTIO_SYSTEM_WINDOWS
else if (kind == RKTIO_ERROR_KIND_WINDOWS) {
wchar_t mbuf[256];
int len;
if ((type != 'e') && !es) {
if ((len = FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS),
NULL,
en, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
mbuf, 255, NULL))) {
if (len == 255)
mbuf[254] = 0;
else
mbuf[len] = 0;
es = NARROW_PATH_copy(mbuf);
/* Remove newlines: */
for (i = strlen(es) - 1; i > 0; i--) {
if (isspace(es[i]))
es[i] = 0;
else
break;
}
if (rktio->last_err_str)
free(rktio->last_err_str);
rktio->last_err_str = es;
return es;
}
}
}
#endif
if (s) return s; if (s) return s;
return "???"; return "???";
} }
@ -61,3 +128,11 @@ const char *rktio_get_last_error_string(rktio_t *rktio)
rktio_get_last_error_kind(rktio), rktio_get_last_error_kind(rktio),
rktio_get_last_error(rktio)); rktio_get_last_error(rktio));
} }
void rktio_error_clean(rktio_t *rktio)
{
#ifdef RKTIO_SYSTEM_WINDOWS
if (rktio->last_error_str)
free(rktio->last_err_str);
#endif
}

View File

@ -659,6 +659,7 @@ int rktio_set_current_directory(rktio_t *rktio, const char *path)
static rktio_identity_t *get_identity(rktio_t *rktio, rktio_fd_t *fd, char *path, int follow_links) static rktio_identity_t *get_identity(rktio_t *rktio, rktio_fd_t *fd, char *path, int follow_links)
{ {
uintptr_t devi = 0, inoi = 0, inoi2 = 0; uintptr_t devi = 0, inoi = 0, inoi2 = 0;
uintptr_t devi_bits = 0, inoi_bits = 0, inoi2_bits = 0;
#ifdef FILES_HAVE_FDS #ifdef FILES_HAVE_FDS
int errid = 0; int errid = 0;
@ -684,6 +685,8 @@ static rktio_identity_t *get_identity(rktio_t *rktio, rktio_fd_t *fd, char *path
/* Warning: we assume that dev_t and ino_t fit in a pointer-sized integer. */ /* Warning: we assume that dev_t and ino_t fit in a pointer-sized integer. */
devi = (uintptr_t)buf.st_dev; devi = (uintptr_t)buf.st_dev;
inoi = (uintptr_t)buf.st_ino; inoi = (uintptr_t)buf.st_ino;
devi_bits = sizeof(buf.st_dev) << 3;
inoi_bits = sizeof(buf.st_ino) << 3;
} }
#endif #endif
#ifdef WINDOWS_FILE_HANDLES #ifdef WINDOWS_FILE_HANDLES
@ -721,6 +724,10 @@ static rktio_identity_t *get_identity(rktio_t *rktio, rktio_fd_t *fd, char *path
devi = info.dwVolumeSerialNumber; devi = info.dwVolumeSerialNumber;
inoi = info.nFileIndexLow; inoi = info.nFileIndexLow;
inoi2 = info.nFileIndexHigh; inoi2 = info.nFileIndexHigh;
devi_bits = 32;
inoi_bits = 32;
inoi2_bits = 32;
#endif #endif
{ {
@ -729,8 +736,11 @@ static rktio_identity_t *get_identity(rktio_t *rktio, rktio_fd_t *fd, char *path
id = malloc(sizeof(rktio_identity_t)); id = malloc(sizeof(rktio_identity_t));
id->a = devi; id->a = devi;
id->a_bits = devi_bits;
id->b = inoi; id->b = inoi;
id->b_bits = inoi_bits;
id->c = inoi2; id->c = inoi2;
id->c_bits = inoi2_bits;
return id; return id;
} }
@ -837,6 +847,7 @@ int rktio_rename_file(rktio_t *rktio, char *dest, char *src, int exists_ok)
} }
char *rktio_readlink(rktio_t *rktio, char *fullfilename) char *rktio_readlink(rktio_t *rktio, char *fullfilename)
/* fullfilename must not have a trailing separator */
{ {
#ifdef RKTIO_SYSTEM_WINDOWS #ifdef RKTIO_SYSTEM_WINDOWS
int is_link; int is_link;
@ -855,6 +866,9 @@ char *rktio_readlink(rktio_t *rktio, char *fullfilename)
len = readlink(fullfilename, buffer, buf_len); len = readlink(fullfilename, buffer, buf_len);
if (len == -1) { if (len == -1) {
if (errno != EINTR) { if (errno != EINTR) {
if (errno == EINVAL)
set_racket_error(RKTIO_ERROR_NOT_A_LINK);
else
get_posix_error(); get_posix_error();
return NULL; return NULL;
} }
@ -1072,7 +1086,7 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
{ {
# ifdef NO_STAT_PROC # ifdef NO_STAT_PROC
set_racket_error(RKTIO_ERROR_UNSUPPORTED); set_racket_error(RKTIO_ERROR_UNSUPPORTED);
return -1; return RKTIO_PERMISSION_ERROR;
# else # else
# ifdef RKTIO_SYSTEM_UNIX # ifdef RKTIO_SYSTEM_UNIX
/* General strategy for permissions (to deal with setuid) /* General strategy for permissions (to deal with setuid)
@ -1097,7 +1111,7 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
if (ok && (errno != EACCES)) { if (ok && (errno != EACCES)) {
get_posix_error(); get_posix_error();
return -1; return RKTIO_PERMISSION_ERROR;
} else { } else {
do { do {
ok = access(filename, W_OK); ok = access(filename, W_OK);
@ -1110,7 +1124,7 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
since the read test succeeded.) */ since the read test succeeded.) */
if (ok && (errno != EACCES) && (errno != EPERM) && (errno != EROFS)) { if (ok && (errno != EACCES) && (errno != EPERM) && (errno != EROFS)) {
get_posix_error(); get_posix_error();
return -1; return RKTIO_PERMISSION_ERROR;
} else { } else {
do { do {
ok = access(filename, X_OK); ok = access(filename, X_OK);
@ -1122,11 +1136,11 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
not executable. */ not executable. */
if (ok && (errno != EACCES) && (errno != EPERM)) { if (ok && (errno != EACCES) && (errno != EPERM)) {
get_posix_error(); get_posix_error();
return -1; return RKTIO_PERMISSION_ERROR;
} else { } else {
return ((read ? S_IRUSR : 0) return ((read ? RKTIO_PERMISSION_READ : 0)
| (write ? S_IWUSR : 0) | (write ? RKTIO_PERMISSION_WRITE : 0)
| (execute ? S_IXUSR : 0)); | (execute ? RKTIO_PERMISSION_EXEC : 0));
} }
} }
} }
@ -1143,7 +1157,7 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
if (cr) { if (cr) {
get_posix_error(); get_posix_error();
return -1; return RKTIO_PERMISSION_ERROR;
} else { } else {
if (all_bits) { if (all_bits) {
int bits = buf.st_mode; int bits = buf.st_mode;
@ -1178,9 +1192,9 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
execute = !!(buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)); execute = !!(buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
# endif # endif
return ((read ? S_IRUSR : 0) return ((read ? RKTIO_PERMISSION_READ : 0)
| (write ? S_IWUSR : 0) | (write ? RKTIO_PERMISSION_WRITE : 0)
| (execute ? S_IXUSR : 0)); | (execute ? RKTIO_PERMISSION_EXEC : 0));
} }
} }
} }
@ -1195,7 +1209,7 @@ int rktio_get_file_or_directory_permissions(rktio_t *rktio, char *filename, int
else else
return flags; return flags;
} else { } else {
return -1; return RKTIO_PERMISSION_ERROR;
} }
} }
# endif # endif
@ -1299,7 +1313,7 @@ struct rktio_directory_list_t {
FF_TYPE info; FF_TYPE info;
}; };
rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename, int is_drive) rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename)
/* path must be normalized */ /* path must be normalized */
{ {
char *pattern; char *pattern;
@ -1389,20 +1403,24 @@ char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl)
} }
} }
FIND_CLOSE(dl->hfile); rktio_directory_list_stop(rktio, dl);
free(dl);
return ""; return "";
} }
void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl)
{
FIND_CLOSE(dl->hfile);
free(dl);
}
# elif !defined(NO_READDIR) # elif !defined(NO_READDIR)
struct rktio_directory_list_t { struct rktio_directory_list_t {
DIR *dir; DIR *dir;
}; };
rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename, int is_drive) rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename)
{ {
rktio_directory_list_t *dl; rktio_directory_list_t *dl;
DIR *dir; DIR *dir;
@ -1445,15 +1463,20 @@ char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl)
return strndup(e->d_name, nlen); return strndup(e->d_name, nlen);
} }
closedir(dl->dir); rktio_directory_list_stop(rktio, dl);
free(dl);
return ""; return "";
} }
void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl)
{
closedir(dl->dir);
free(dl);
}
#else #else
rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename, int is_drive) rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename)
{ {
set_racket_error(RKTIO_ERROR_UNSUPPORTED); set_racket_error(RKTIO_ERROR_UNSUPPORTED);
return NULL; return NULL;
@ -1465,6 +1488,10 @@ char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl)
return NULL; return NULL;
} }
void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl)
{
}
#endif #endif
/*========================================================================*/ /*========================================================================*/

View File

@ -36,6 +36,7 @@ rktio_t *rktio_init(void)
void rktio_destroy(rktio_t *rktio) void rktio_destroy(rktio_t *rktio)
{ {
rktio_error_clean(rktio);
rktio_process_deinit(rktio); rktio_process_deinit(rktio);
rktio_free_ghbn(rktio); rktio_free_ghbn(rktio);
rktio_free_global_poll_set(rktio); rktio_free_global_poll_set(rktio);

View File

@ -33,6 +33,9 @@
struct rktio_t { struct rktio_t {
intptr_t errid; intptr_t errid;
int errkind; int errkind;
#ifdef RKTIO_SYSTEM_WINDOWS
char *last_err_str;
#endif
#ifdef RKTIO_SYSTEM_UNIX #ifdef RKTIO_SYSTEM_UNIX
struct group_member_cache_entry_t *group_member_cache; struct group_member_cache_entry_t *group_member_cache;
@ -252,6 +255,8 @@ void rktio_set_windows_error(rktio_t *rktio, int errid);
# define set_windows_error(errid) rktio_set_windows_error(rktio, errid) # define set_windows_error(errid) rktio_set_windows_error(rktio, errid)
#endif #endif
void rktio_error_clean(rktio_t *rktio);
#if defined(USE_FNDELAY_O_NONBLOCK) #if defined(USE_FNDELAY_O_NONBLOCK)
# define RKTIO_NONBLOCKING FNDELAY # define RKTIO_NONBLOCKING FNDELAY
#else #else