Mail Archives: djgpp-workers/2002/02/02/05:51:33
Hello.
Please find below a revised version of my patch to add /dev/zero
and /dev/full support to DJGPP. The last patch was sent on 2001-01-24.
Here are the changes since the last patch:
* handle restarts by checking __bss_count;
* include <libc/stubs.h>.
OK to commit?
Thanks, bye, Rich
--- /develop/djgpp/include/sys/xdevices.h Thu Jan 1 00:00:00 1970
+++ /develop/djgpp.rw/include/sys/xdevices.h Sat Feb 2 10:40:56 2002
@@ -0,0 +1,29 @@
+/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
+
+#ifndef __dj_include_sys_xdevices_h_
+#define __dj_include_sys_xdevices_h_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __dj_ENFORCE_ANSI_FREESTANDING
+
+#ifndef __STRICT_ANSI__
+
+#ifndef _POSIX_SOURCE
+
+int __install_dev_zero (void);
+int __install_dev_full (void);
+
+#endif /* !_POSIX_SOURCE */
+#endif /* !__STRICT_ANSI__ */
+#endif /* !__dj_ENFORCE_ANSI_FREESTANDING */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !__dj_include_sys_xdevices_h_ */
--- /develop/djgpp/src/docs/kb/wc204.txi Thu Jan 31 23:00:12 2002
+++ /develop/djgpp.rw/src/docs/kb/wc204.txi Sat Feb 2 10:41:30 2002
@@ -714,3 +714,11 @@ the @env{DJGPP} environment variable is
The functions @code{fstatvfs} and @code{statvfs} have been added.
@code{statvfs} does much the same as @code{statfs}, but it is
POSIX-compliant.
+
+@cindex @code{/dev/zero}
+@findex __install_dev_zero AT r{, install support for @file{/dev/zero}}
+@cindex @code{/dev/full}
+@findex __install_dev_full AT r{, install support for @file{/dev/full}}
+Optional support for the devices @file{/dev/zero} and @file{/dev/full} was
+added. This is enabled using the @code{__install_dev_zero} and
+@code{__install_dev_zero} functions.
--- /develop/djgpp/src/libc/fsext/fse_zero.c Thu Jan 1 00:00:00 1970
+++ /develop/djgpp.rw/src/libc/fsext/fse_zero.c Sat Feb 2 10:39:52 2002
@@ -0,0 +1,678 @@
+/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
+
+/*
+ * fse_zero.c - An implementation of /dev/zero and /dev/full for DJGPP
+ */
+
+#include <libc/stubs.h>
+#include <libc/bss.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <time.h>
+#include <fcntl.h>
+#include <io.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/fsext.h>
+#include <sys/xdevices.h>
+
+static const char DEV_ZERO_PATH[] = "/dev/zero";
+static const char DEV_FULL_PATH[] = "/dev/full";
+
+typedef struct {
+ int flags;
+ int dev_full:1; /* 1 => /dev/full, 0 => /dev/zero */
+ int dup_count; /* Support for dup() - reference count. */
+} DEV_DATA;
+
+static int dev_fsext_installed = 0;
+static int dev_zero_installed = 0;
+static int dev_full_installed = 0;
+
+/* stat(), fstat() support */
+static time_t dev_zero_atime;
+static time_t dev_zero_ctime;
+static ino_t dev_zero_inode = 0;
+static time_t dev_zero_mtime;
+
+static time_t dev_full_atime;
+static time_t dev_full_ctime;
+static ino_t dev_full_inode = 0;
+static time_t dev_full_mtime;
+
+extern ino_t _invent_inode (const char *name,
+ unsigned time_stamp,
+ unsigned long fsize);
+
+static int dev_fsext (__FSEXT_Fnumber n, int *rv, va_list args);
+
+/* ----------------
+ * - internal_dup -
+ * ---------------- */
+
+static int
+internal_dup (const int fd)
+{
+ DEV_DATA *data = NULL;
+ int new_fd = 0;
+
+ /* Allocate a new file descriptor. */
+ new_fd = __FSEXT_alloc_fd(dev_fsext);
+ if (new_fd < 0)
+ return(-1);
+
+ /* Get context */
+ data = (DEV_DATA *) __FSEXT_get_data(fd);
+ if (data == NULL) {
+ errno = EBADF;
+ _close(new_fd);
+ return(-1);
+ }
+
+ /* Associate the context with the new fd too. */
+ if (__FSEXT_set_data(new_fd, (void *) data) == NULL) {
+ errno = ENOMEM;
+ _close(new_fd);
+ return(-1);
+ }
+
+ data->dup_count++;
+ return(new_fd);
+}
+
+/* ------------------------
+ * - internal_stat_simple -
+ * ------------------------ */
+
+/* This sets up the "obvious" fields of 'struct stat'. */
+
+static int inline
+internal_stat_simple (struct stat *sbuf)
+{
+ sbuf->st_dev = -1;
+ sbuf->st_gid = getgid();
+ /* Everyone can read & write the zero device; it's a character device. */
+ sbuf->st_mode = S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH|S_IFCHR;
+ sbuf->st_nlink = 1;
+ sbuf->st_size = 0;
+ sbuf->st_blksize = 0;
+ sbuf->st_uid = getuid();
+
+#ifdef HAVE_ST_RDEV
+ sbuf->st_rdev = -1;
+#endif
+
+ return(1);
+}
+
+/* --------------------------
+ * - dev_zero_internal_stat -
+ * -------------------------- */
+
+static int
+dev_zero_internal_stat (struct stat *sbuf)
+{
+ internal_stat_simple(sbuf);
+
+ sbuf->st_atime = dev_zero_atime;
+ sbuf->st_ctime = dev_zero_ctime;
+ sbuf->st_ino = dev_zero_inode;
+ sbuf->st_mtime = dev_zero_mtime;
+
+ return(1);
+}
+
+/* --------------------------
+ * - dev_full_internal_stat -
+ * -------------------------- */
+
+/* This sets up the "obvious" fields of 'struct stat' for full device. */
+
+static int
+dev_full_internal_stat (struct stat *sbuf)
+{
+ internal_stat_simple(sbuf);
+
+ sbuf->st_atime = dev_full_atime;
+ sbuf->st_ctime = dev_full_ctime;
+ sbuf->st_ino = dev_full_inode;
+ sbuf->st_mtime = dev_full_mtime;
+
+ return(1);
+}
+
+/* ------------------
+ * - match_dev_path -
+ * ------------------ */
+
+static int inline
+match_dev_path (const char *filename, const char *dev_path)
+{
+ int ret = 1; /* Successful match by default */
+
+ if (filename[0] && (filename[1] == ':')) {
+ /* Drive-extended */
+ if (stricmp(filename + 2, dev_path) != 0) {
+ ret = 0;
+ }
+ } else {
+ /* Normal */
+ if (stricmp(filename, dev_path) != 0) {
+ ret =0;
+ }
+ }
+
+ return(ret);
+}
+
+/* -------------
+ * - dev_fsext -
+ * ------------- */
+
+static int
+dev_fsext (__FSEXT_Fnumber n, int *rv, va_list args)
+{
+ int emul = 0; /* Emulated call? 1 => yes, 0 = no. */
+ int fd = 0;
+ DEV_DATA *data = NULL;
+ char *filename = NULL;
+ int open_mode = 0;
+ int perm = 0;
+ mode_t creat_mode = 0;
+ void *buf = NULL;
+ size_t buflen = 0;
+ off_t offset = 0;
+ int whence = 0;
+ struct stat *sbuf = NULL;
+ int cmd = 0;
+ int iparam = 0;
+#ifdef DJGPP_SUPPORTS_FIONBIO_NOW
+ int *piparam = NULL;
+#endif /* DJGPP_SUPPORTS_FIONBIO_NOW */
+
+ switch(n) {
+ default:
+ case __FSEXT_nop:
+ break;
+
+ case __FSEXT_open:
+ filename = va_arg(args, char *);
+ open_mode = va_arg(args, int);
+
+ if (open_mode & O_CREAT)
+ perm = va_arg(args, int);
+
+ /* Check the filename */
+ if ( !match_dev_path(filename, DEV_ZERO_PATH)
+ && !match_dev_path(filename, DEV_FULL_PATH))
+ break;
+
+ /* It's for us. */
+ emul = 1;
+
+ /* Check whether the zero/full device has been installed. */
+ if (match_dev_path(filename, DEV_ZERO_PATH) && !dev_zero_installed)
+ break;
+
+ if (match_dev_path(filename, DEV_FULL_PATH) && !dev_full_installed)
+ break;
+
+ /* zero device _always_ exists. */
+ if (open_mode & O_CREAT) {
+ errno = EEXIST;
+ *rv = -1;
+ break;
+ }
+
+ /* Allocate a file descriptor for the device. */
+ fd = __FSEXT_alloc_fd(dev_fsext);
+ if (fd < 0) {
+ *rv = fd;
+ break;
+ }
+
+ /* Allocate some fd context. */
+ data = (DEV_DATA *) malloc(sizeof(*data));
+ if (data == NULL) {
+ errno = ENOMEM;
+ *rv = -1;
+ break;
+ }
+
+ /* Set up the context. */
+ memset(data, 0, sizeof(*data));
+
+ data->flags = open_mode; /* Save open mode for read(), write() */
+ data->dup_count = 1;
+
+ /* Is this zero or full device? */
+ if (match_dev_path(filename, DEV_FULL_PATH))
+ data->dev_full = 1;
+ else
+ data->dev_full = 0;
+
+ /* zero device always has O_NOINHERIT. */
+ data->flags |= O_NOINHERIT;
+
+ /* Ensure that we only have relevant flags. */
+ data->flags &= (O_ACCMODE | O_NOINHERIT | O_NONBLOCK);
+
+ /* Associate the context with the fd. */
+ if (__FSEXT_set_data(fd, (void *) data) == NULL) {
+ errno = ENOMEM;
+ *rv = -1;
+ break;
+ }
+
+ /* Done */
+ *rv = fd;
+ break;
+
+ case __FSEXT_creat:
+ filename = va_arg(args, char *);
+ creat_mode = va_arg(args, mode_t);
+
+ /* Check the filename */
+ if ( !match_dev_path(filename, DEV_ZERO_PATH)
+ && !match_dev_path(filename, DEV_FULL_PATH))
+ break;
+
+ /* Check whether the zero/full device has been installed. */
+ if (match_dev_path(filename, DEV_ZERO_PATH) && !dev_zero_installed)
+ break;
+
+ if (match_dev_path(filename, DEV_FULL_PATH) && !dev_full_installed)
+ break;
+
+ /* zero device _always_ exists. */
+ emul = 1;
+ errno = EEXIST;
+ *rv = -1;
+ break;
+
+ case __FSEXT_read:
+ fd = va_arg(args, int);
+ buf = va_arg(args, void *);
+ buflen = va_arg(args, size_t);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Get context */
+ data = (DEV_DATA *) __FSEXT_get_data(fd);
+ if (data == NULL) {
+ errno = EBADF;
+ *rv = -1;
+ break;
+ }
+
+ /* Can we actually read from the zero device? */
+ if ( ((data->flags & O_ACCMODE) != O_RDONLY)
+ && ((data->flags & O_ACCMODE) != O_RDWR) ) {
+ errno = EACCES;
+ *rv = -1;
+ break;
+ }
+
+ /* Now read - just zero the buffer. */
+ memset(buf, '\0', buflen);
+
+ /* Update access time */
+ if (data->dev_full)
+ time(&dev_full_atime);
+ else
+ time(&dev_zero_atime);
+
+ *rv = (int) buflen;
+ break;
+
+ case __FSEXT_write:
+ fd = va_arg(args, int);
+ buf = va_arg(args, void *);
+ buflen = va_arg(args, size_t);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Get context */
+ data = (DEV_DATA *) __FSEXT_get_data(fd);
+ if (data == NULL) {
+ errno = EBADF;
+ *rv = -1;
+ break;
+ }
+
+ /* Can we actually write to the zero device? */
+ if ( ((data->flags & O_ACCMODE) != O_WRONLY)
+ && ((data->flags & O_ACCMODE) != O_RDWR) ) {
+ errno = EACCES;
+ *rv = -1;
+ break;
+ }
+
+ if (data->dev_full) {
+ /* Nope, it's full */
+ errno = ENOSPC;
+ *rv = -1;
+ break;
+ }
+
+ /* Now write - just ignore the buffer. */
+
+ /* Update access & modification times - it must be zero device here. */
+ time(&dev_zero_atime);
+ time(&dev_zero_mtime);
+
+ *rv = (int) buflen;
+ break;
+
+ case __FSEXT_ready:
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ *rv = __FSEXT_ready_read | __FSEXT_ready_write;
+ break;
+
+ case __FSEXT_close:
+ fd = va_arg(args, int);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Get context */
+ data = (DEV_DATA *) __FSEXT_get_data(fd);
+ if (data == NULL) {
+ errno = EBADF;
+ *rv = -1;
+ break;
+ }
+
+ __FSEXT_set_data(fd, NULL);
+ __FSEXT_set_function(fd, NULL);
+
+ /* Cope with dup()'d zero devices. */
+ data->dup_count--;
+
+ if (data->dup_count <= 0) {
+ /* No longer referenced */
+ free(data);
+ }
+
+ _close(fd);
+ break;
+
+ case __FSEXT_fcntl:
+ fd = va_arg(args, int);
+ cmd = va_arg(args, int);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Get context */
+ data = (DEV_DATA *) __FSEXT_get_data(fd);
+ if (data == NULL) {
+ errno = EBADF;
+ *rv = -1;
+ break;
+ }
+
+ switch(cmd) {
+ case F_DUPFD:
+ *rv = internal_dup(fd);
+ break;
+
+ case F_GETFD:
+ if (data->flags & O_NOINHERIT)
+ *rv = 1;
+ else
+ *rv = 0;
+ break;
+
+ case F_SETFD:
+ iparam = va_arg(args, int);
+
+ /* Unsupported - this can't be changed on DOS/Windows. */
+ errno = ENOSYS;
+ *rv = -1;
+ break;
+
+ case F_GETFL:
+ *rv = data->flags;
+ break;
+
+ case F_SETFL:
+ iparam = va_arg(args, int);
+
+ /* Trying to change immutable fields? */
+ if ((iparam & ~O_NONBLOCK) != (data->flags & ~O_NONBLOCK)) {
+ errno = ENOSYS;
+ *rv = -1;
+ break;
+ }
+
+ /* Handle mutable fields */
+ if (iparam & O_NONBLOCK)
+ data->flags |= O_NONBLOCK;
+ else
+ data->flags &= ~O_NONBLOCK;
+
+ *rv = 0;
+ break;
+
+ default:
+ /* Unknown/unhandled fcntl */
+ errno = ENOSYS;
+ *rv = -1;
+ break;
+ }
+ break;
+
+ case __FSEXT_ioctl:
+ fd = va_arg(args, int);
+ cmd = va_arg(args, int);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ switch(cmd) {
+ /* */
+#ifdef DJGPP_SUPPORTS_FIONBIO_NOW
+ case FIONBIO:
+ piparam = va_arg(args, int *);
+ if (*piparam)
+ data->flags |= O_NONBLOCK;
+ else
+ data->flags &= ~O_NONBLOCK;
+
+ *rv = 0;
+ break;
+#endif /* DJGPP_SUPPORTS_FIONBIO_NOW */
+
+ default:
+ errno = ENOTTY;
+ *rv = 1;
+ break;
+ }
+ break;
+
+ case __FSEXT_lseek:
+ fd = va_arg(args, int);
+ offset = va_arg(args, off_t);
+ whence = va_arg(args, int);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ *rv = 0;
+ break;
+
+ case __FSEXT_link:
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Fail request */
+ errno = EPERM;
+ *rv = -1;
+ break;
+
+ case __FSEXT_unlink:
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* The zero device cannot be removed. */
+ errno = EPERM;
+ *rv = -1;
+ break;
+
+ case __FSEXT_dup:
+#ifdef DJGPP_SUPPORTS_FSEXT_DUP_NOW
+ fd = va_arg(args, int);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Done */
+ *rv = internal_dup(fd);
+#endif /* DJGPP_SUPPORTS_FSEXT_DUP_NOW */
+ break;
+
+ case __FSEXT_dup2:
+#ifdef DJGPP_SUPPORTS_FSEXT_DUP2_NOW
+ /* TODO: When __FSEXT_dup2 is supported, add support. */
+#endif /* DJGPP_SUPPORTS_FSEXT_DUP2_NOW */
+ break;
+
+ case __FSEXT_stat:
+ filename = va_arg(args, char *);
+ sbuf = va_arg(args, struct stat *);
+
+ /* Check the filename */
+ if ( !match_dev_path(filename, DEV_ZERO_PATH)
+ && !match_dev_path(filename, DEV_FULL_PATH))
+ break;
+
+ /* Check whether the zero/full device has been installed. */
+ if (match_dev_path(filename, DEV_ZERO_PATH) && !dev_zero_installed)
+ break;
+
+ if (match_dev_path(filename, DEV_FULL_PATH) && !dev_full_installed)
+ break;
+
+ /* It's for us. */
+ emul = 1;
+
+ /* Set up the stat buf */
+ memset(sbuf, 0, sizeof(*sbuf));
+
+ if (match_dev_path(filename, DEV_FULL_PATH))
+ dev_full_internal_stat(sbuf);
+ else
+ dev_zero_internal_stat(sbuf);
+
+ *rv = 0;
+ break;
+
+ case __FSEXT_fstat:
+ fd = va_arg(args, int);
+ sbuf = va_arg(args, struct stat *);
+
+ /* This must be emulated, since the FSEXT has been called. */
+ emul = 1;
+
+ /* Get context */
+ data = (DEV_DATA *) __FSEXT_get_data(fd);
+ if (data == NULL) {
+ errno = EBADF;
+ *rv = -1;
+ break;
+ }
+
+ /* Set up the stat buf */
+ memset(sbuf, 0, sizeof(*sbuf));
+
+ if (data->dev_full)
+ dev_full_internal_stat(sbuf);
+ else
+ dev_zero_internal_stat(sbuf);
+
+ *rv = 0;
+ break;
+ }
+
+ return(emul);
+}
+
+/* -------------
+ * - check_bss -
+ * ------------- */
+
+/* Force initialisation in restarted programs, e.g. Emacs. */
+
+static int dev_zero_bss_count = -1;
+
+static void
+check_bss_count (void)
+{
+ if (dev_zero_bss_count == __bss_count)
+ return;
+
+ /* Save __bss_count. */
+ dev_zero_bss_count = __bss_count;
+
+ /* Force (re-)initialisation. */
+ dev_fsext_installed = dev_zero_installed = dev_full_installed = 0;
+}
+
+/* ----------------------
+ * - __install_dev_zero -
+ * ---------------------- */
+
+int
+__install_dev_zero (void)
+{
+ check_bss_count();
+
+ if (dev_zero_installed)
+ return(dev_zero_installed);
+
+ if (!dev_fsext_installed) {
+ __FSEXT_add_open_handler(dev_fsext);
+ dev_fsext_installed = 1;
+ }
+
+ time(&dev_zero_ctime);
+ dev_zero_atime = dev_zero_mtime = dev_zero_ctime;
+ dev_zero_inode = _invent_inode(DEV_ZERO_PATH, 0, 0);
+ dev_zero_installed = 1;
+
+ return(1);
+}
+
+/* ----------------------
+ * - __install_dev_full -
+ * ---------------------- */
+
+int
+__install_dev_full (void)
+{
+ check_bss_count();
+
+ if (dev_full_installed)
+ return(dev_full_installed);
+
+ if (!dev_fsext_installed) {
+ __FSEXT_add_open_handler(dev_fsext);
+ dev_fsext_installed = 1;
+ }
+
+ time(&dev_full_ctime);
+ dev_full_atime = dev_full_mtime = dev_full_ctime;
+ dev_full_inode = _invent_inode(DEV_FULL_PATH, 0, 0);
+ dev_full_installed = 1;
+
+ return(1);
+}
--- /develop/djgpp/src/libc/fsext/fse_zero.txh Thu Jan 1 00:00:00 1970
+++ /develop/djgpp.rw/src/libc/fsext/fse_zero.txh Sat Feb 2 10:24:32 2002
@@ -0,0 +1,55 @@
+@node __install_dev_zero, file system
+
+@subheading Syntax
+
+@example
+#include <sys/xdevices.h>
+
+int __install_dev_zero (void);
+@end example
+
+@subheading Description
+
+This function activates support for the special file @file{/dev/zero}.
+When read, @file{dev/zero} always returns @samp{\0} characters. When
+written, @file{/dev/zero} discards the data. Seeks on @file{/dev/zero}
+will always succeed.
+
+The DJGPP debug support functions will interfere with @file{/dev/zero}
+(@pxref{File System Extensions}).
+
+@subheading Return Value
+
+On success, a non-zero value is returned; on failure, zero is returned.
+
+@subheading Portability
+
+@portability !ansi, !posix
+
+@node __install_dev_full, file system
+
+@subheading Syntax
+
+@example
+#include <sys/xdevices.h>
+
+int __install_dev_full (void);
+@end example
+
+@subheading Description
+
+This function activates support for the special file @file{/dev/full}.
+When read, @file{dev/full} always returns @samp{\0} characters. Writes
+to @file{/dev/full} will fail with @code{errno} set to @code{ENOSPC}.
+Seeks on @file{/dev/full} always succeed.
+
+The DJGPP debug support functions will interfere with @file{/dev/full}
+(@pxref{File System Extensions}).
+
+@subheading Return Value
+
+On success, a non-zero value is returned; on failure, zero is returned.
+
+@subheading Portability
+
+@portability !ansi, !posix
--- /develop/djgpp/src/libc/fsext/makefile Sat Nov 25 20:38:58 1995
+++ /develop/djgpp.rw/src/libc/fsext/makefile Sat Feb 2 10:40:24 2002
@@ -1,7 +1,11 @@
+# Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details
+# Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details
+# Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details
# Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details
TOP=..
SRC += fse_open.c
SRC += fsext.c
+SRC += fse_zero.c
include $(TOP)/../makefile.inc
--- /develop/djgpp/tests/libc/fsext/tzero.c Thu Jan 1 00:00:00 1970
+++ /develop/djgpp.rw/tests/libc/fsext/tzero.c Sat Feb 2 10:40:10 2002
@@ -0,0 +1,537 @@
+/* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */
+/* Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details */
+
+/* Test written by Richard Dawe <richdawe AT bigfoot DOT com> */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <assert.h>
+
+#include <sys/xdevices.h>
+
+static const char DEV_ZERO_PATH[] = "/dev/zero";
+static const char DEV_FULL_PATH[] = "/dev/full";
+
+static int
+jumble_buffer (char *buf, size_t buflen)
+{
+ size_t i;
+
+ for (i = 0; i < buflen; i++) {
+ buf[i] = random() % 0xff;
+ }
+
+ return(1);
+}
+
+static int
+dump_stat (struct stat *sbuf)
+{
+ printf("st_atime = %d\n"
+ "st_ctime = %d\n"
+ "st_dev = %d\n"
+ "st_gid = %d\n"
+ "st_ino = %d\n"
+ "st_mode = %d\n"
+ "st_mtime = %d\n"
+ "st_nlink = %d\n"
+ "st_size = %d\n"
+ "st_blksize = %d\n"
+ "st_uid = %d\n",
+ sbuf->st_atime, sbuf->st_ctime, sbuf->st_dev,
+ sbuf->st_gid, sbuf->st_ino, sbuf->st_mode,
+ sbuf->st_mtime, sbuf->st_nlink, sbuf->st_size,
+ sbuf->st_blksize, sbuf->st_uid);
+
+ return(1);
+}
+
+static int
+test_fstat (const char *filename)
+{
+ struct stat sbuf;
+ char buf[32768];
+ int fd = 0;
+ int n = 0;
+
+ fd = open(filename, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", filename, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ sleep(1);
+
+ n = read(fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to read %lu bytes from %s: %s\n",
+ sizeof(buf), filename, strerror(errno));
+ }
+
+ sleep(1);
+
+ n = write(fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to write %lu bytes to %s: %s\n",
+ sizeof(buf), filename, strerror(errno));
+ }
+
+ if (fstat(fd, &sbuf) < 0) {
+ fprintf(stderr,
+ "Unable to fstat() %s: %s\n", filename, strerror(errno));
+ close(fd);
+ return(0);
+ }
+
+ printf("fstat() result for %s:\n", filename);
+ dump_stat(&sbuf);
+ printf("\n");
+
+ close(fd);
+
+ return(1);
+}
+
+int
+main (int argc, char *argv[])
+{
+ char buf[32768];
+ char filename[PATH_MAX];
+ int fd = 0;
+ int new_fd = 0;
+ fd_set readfds, writefds;
+ struct timeval tv;
+ struct stat sbuf;
+ off_t offset = 0;
+ off_t ret_offset = 0;
+ int n = 0;
+ size_t i = 0;
+
+ if (!__install_dev_zero()) {
+ fprintf(stderr, "__install_dev_zero() failed\n");
+ return(EXIT_FAILURE);
+ }
+
+ if (!__install_dev_full()) {
+ fprintf(stderr, "__install_dev_full() failed\n");
+ return(EXIT_FAILURE);
+ }
+
+ /* - Test open() - */
+
+ /* Normal path */
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ fd = open(DEV_FULL_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_FULL_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* Drive-extended path */
+ sprintf(filename, "c:%s", DEV_ZERO_PATH);
+
+ fd = open(filename, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", filename, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* Upper case */
+ strcpy(filename, DEV_ZERO_PATH);
+ strupr(filename);
+
+ fd = open(filename, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", filename, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* - Generic tests of /dev/zero. - */
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ /* Write buffer into /dev/zero. */
+ jumble_buffer(buf, sizeof(buf));
+
+ n = write(fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to write %lu bytes to %s: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ /* Zero buffer by reading from /dev/zero. */
+ n = read(fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to read %lu bytes from %s: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ for (i = 0; i < sizeof(buf); i++) {
+ if (buf[i] != '\0') {
+ fprintf(stderr, "Byte %lu in read data is non-zero\n", i);
+ return(EXIT_FAILURE);
+ }
+ }
+
+ close(fd);
+
+ /* - Generic tests for /dev/full - */
+
+ /* Writing to /dev/full is tested by test_fstat() later on. */
+
+ /* - Test /dev/zero opened read-only. - */
+ fd = open(DEV_ZERO_PATH, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s read-only: %s\n",
+ DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ /* Check that writing fails. */
+ jumble_buffer(buf, sizeof(buf));
+
+ n = write(fd, buf, sizeof(buf));
+ if (n >= 0) {
+ fprintf(stderr,
+ "Able to write %lu bytes to %s when read-only: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ /* Zero buffer by reading from /dev/zero. */
+ n = read(fd, buf, sizeof(buf));
+
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to read %lu bytes from %s when read-only: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ close(fd);
+
+ /* - Test /dev/zero opened write-only. - */
+ fd = open(DEV_ZERO_PATH, O_WRONLY);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s write-only: %s\n",
+ DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ /* Write buffer into /dev/zero. */
+ jumble_buffer(buf, sizeof(buf));
+
+ n = write(fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to write %lu bytes to %s when write-only: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ /* Check that reading fails. */
+ n = read(fd, buf, sizeof(buf));
+ if (n >= 0) {
+ fprintf(stderr,
+ "Able to read %lu bytes from %s when write-only: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* - Check that creat() fails. - */
+ fd = creat(DEV_ZERO_PATH, S_IRUSR|S_IWUSR);
+ if (fd >= 0) {
+ fprintf(stderr,
+ "creat() succeeded in creating %s - it should fail\n",
+ DEV_ZERO_PATH);
+ return(EXIT_FAILURE);
+ }
+
+ assert(errno == EEXIST);
+
+ /* - Check that open() fails, when using O_CREAT. - */
+ fd = open(DEV_ZERO_PATH, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
+ if (fd >= 0) {
+ fprintf(stderr,
+ "open() succeeded in creating %s - it should fail\n",
+ DEV_ZERO_PATH);
+ return(EXIT_FAILURE);
+ }
+
+ assert(errno == EEXIST);
+
+ /* - Check select() support. - */
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ memset(&tv, 0, sizeof(tv));
+
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &writefds);
+
+ n = select(fd + 1, &readfds, &writefds, NULL, &tv);
+ if (n < 0) {
+ fprintf(stderr,
+ "select() on %sfailed: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ if (!FD_ISSET(fd, &readfds)) {
+ fprintf(stderr, "Expected %s to be ready for reading\n", DEV_ZERO_PATH);
+ return(EXIT_FAILURE);
+ }
+
+ if (!FD_ISSET(fd, &writefds)) {
+ fprintf(stderr, "Expected %s to be ready for writing\n", DEV_ZERO_PATH);
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* - Check link() fails - */
+#define LINK_TEST_PATH "/temp/wibble"
+
+ n = link(DEV_ZERO_PATH, LINK_TEST_PATH);
+ if (n == 0) {
+ fprintf(stderr,
+ "link(\"%s\", \"%s\") succeeded - it should fail\n",
+ DEV_ZERO_PATH, LINK_TEST_PATH);
+ return(EXIT_FAILURE);
+ }
+
+ assert(errno == EPERM);
+
+#undef LINK_TEST_PATH
+
+ /* - Check unlink() fails - */
+ n = unlink(DEV_ZERO_PATH);
+ if (n >= 0) {
+ fprintf(stderr,
+ "unlink() succeeded in removing %s - it should fail\n",
+ DEV_ZERO_PATH);
+ return(EXIT_FAILURE);
+ }
+
+ assert(errno == EPERM);
+
+ /* - Check lseek() - */
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < 1000; i++) {
+ offset = (off_t) random();
+ ret_offset = lseek(fd, offset, SEEK_SET);
+
+ if (ret_offset < 0)
+ fprintf(stderr, "lseek() to position %d failed\n", offset);
+ }
+
+ close(fd);
+
+ /* - Check dup works - */
+#ifdef DJGPP_SUPPORTS_FSEXT_DUP_NOW
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ new_fd = dup(fd);
+ if (new_fd < 0) {
+ fprintf(stderr,
+ "Unable do dup file descriptor for %s: %s\n",
+ DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* Zero buffer by reading from /dev/zero. */
+ n = read(new_fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to read %lu bytes from %s: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ close(new_fd);
+#endif /* DJGPP_SUPPORTS_FSEXT_DUP_NOW */
+
+ /* - Check fcntl() - */
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ new_fd = fcntl(fd, F_DUPFD);
+ if (new_fd < 0) {
+ fprintf(stderr, "F_DUPFD fcntl failed: %s\n", strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ /* Zero buffer by reading from /dev/zero. */
+ n = read(new_fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to read %lu bytes from %s: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ /* Close old fd and check we can still read using new_fd. */
+ close(fd);
+
+ n = read(new_fd, buf, sizeof(buf));
+ if (n < 0) {
+ fprintf(stderr,
+ "Unable to read %lu bytes from %s: %s\n",
+ sizeof(buf), DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ assert(((size_t) n) == sizeof(buf));
+
+ close(new_fd);
+
+ /* Now try other fcntls */
+ fd = open(DEV_ZERO_PATH, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr,
+ "Unable to open %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ n = fcntl(fd, F_GETFD);
+ if (n < 0) {
+ fprintf(stderr, "F_GETFD fcntl failed: %s\n", strerror(errno));
+ return(EXIT_FAILURE);
+ }
+ printf("close-on-exec is %s\n", n ? "enabled" : "disabled");
+
+ n = fcntl(fd, F_SETFD, 1);
+ if (n != -1) {
+ fprintf(stderr, "F_SETFD succeeded - it should fail\n");
+ return(EXIT_FAILURE);
+ }
+
+ assert(errno == ENOSYS);
+
+ n = fcntl(fd, F_GETFL);
+ printf("F_GETFL returns: 0x%x\n", n);
+
+ if (n & O_NONBLOCK) {
+ n = fcntl(fd, F_SETFL, n & ~O_NONBLOCK);
+ } else {
+ n = fcntl(fd, F_SETFL, n | O_NONBLOCK);
+ }
+
+ if (n < 0) {
+ fprintf(stderr, "Failed to flip O_NONBLOCK using F_SETFL fcntl: %s\n",
+ strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ /* Try a bogus fcntl */
+ n = fcntl(fd, 0xff);
+ if (n != -1) {
+ fprintf(stderr, "Bogus fcntl succeeded - it should fail\n");
+ return(EXIT_FAILURE);
+ }
+
+ assert(errno == ENOSYS);
+
+ close(fd);
+
+ /* - Check stat() works - */
+ if (stat(DEV_ZERO_PATH, &sbuf) < 0) {
+ fprintf(stderr,
+ "Unable to stat() %s: %s\n", DEV_ZERO_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ printf("stat() result for %s:\n", DEV_ZERO_PATH);
+ dump_stat(&sbuf);
+ printf("\n");
+
+ if (stat(DEV_FULL_PATH, &sbuf) < 0) {
+ fprintf(stderr,
+ "Unable to stat() %s: %s\n", DEV_FULL_PATH, strerror(errno));
+ return(EXIT_FAILURE);
+ }
+
+ printf("stat() result for %s:\n", DEV_FULL_PATH);
+ dump_stat(&sbuf);
+ printf("\n");
+
+ /* - Check fstat() works - */
+ test_fstat(DEV_ZERO_PATH);
+ test_fstat(DEV_FULL_PATH);
+
+ return(EXIT_SUCCESS);
+}
--- /develop/djgpp/tests/libc/fsext/makefile Thu Jan 1 21:33:42 1998
+++ /develop/djgpp.rw/tests/libc/fsext/makefile Sat Feb 2 10:40:46 2002
@@ -1,6 +1,11 @@
+# Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details
+# Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details
+# Copyright (C) 2000 DJ Delorie, see COPYING.DJ for details
+
TOP=..
SRC += open2.c
SRC += pipe.c
+SRC += tzero.c
include $(TOP)/../makefile.inc
- Raw text -