X-Authentication-Warning: delorie.com: mailnull set sender to djgpp-workers-bounces using -f Date: Sat, 02 Feb 2002 10:52:47 +0000 From: "Richard Dawe" Sender: rich AT phekda DOT freeserve DOT co DOT uk To: djgpp-workers AT delorie DOT com X-Mailer: Emacs 21.0.98 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6 Subject: /dev/zero patch Message-Id: Reply-To: djgpp-workers AT delorie DOT com 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 . 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + +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 + +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 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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