Mail Archives: djgpp-workers/2001/01/24/17:23:08
Hello.
Here is the latest version of the patch to add /dev/zero and /dev/full
support to DJGPP, revised after comments (thanks). Please note that the
copyright assignment issue is ongoing. My boss is investigating whether or
not I need to get a disclaimer from my company. So, please make any
further comments on this code.
Now that {f}stat() is fixed, the tests pass. I revised the test to cope
with the result of fseek(), lseek() on /dev/zero and /dev/full. Before it
was checking that the offset was the same as requested. Now it just checks
that the call suceeded, since 0 is always returned as the offset.
Thanks, bye, Rich =]
*** /dev/null Wed Jan 24 22:13:36 2001
--- /develop/djgpp.0/include/sys/xdevices.h Wed Jan 24 21:41:18 2001
***************
*** 0 ****
--- 1,28 ----
+ /* 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.rw/src/docs/kb/wc204.txi Sun Jan 21 13:48:42 2001
--- /develop/djgpp.0/src/docs/kb/wc204.txi Wed Jan 24 22:11:56 2001
*************** an @file{env} directory in the current w
*** 219,221 ****
--- 219,229 ----
functions are passed the @file{/dev/env} file name. This avoids
creating spurious @file{env} directories in any directory where the
standard GNU @code{mkinstalldirs} script is run.
+
+ @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.
*** /dev/null Wed Jan 24 22:13:36 2001
--- /develop/djgpp.0/src/libc/fsext/fse_zero.c Wed Jan 24 21:41:18 2001
***************
*** 0 ****
--- 1,647 ----
+ /*
+ * fse_zero.c - An implementation of /dev/zero and /dev/full for DJGPP
+ */
+
+ #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);
+ }
+
+ /* ----------------------
+ * - __install_dev_zero -
+ * ---------------------- */
+
+ int
+ __install_dev_zero (void)
+ {
+ 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)
+ {
+ 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);
+ }
*** /dev/null Wed Jan 24 22:13:36 2001
--- /develop/djgpp.0/src/libc/fsext/fse_zero.txh Wed Jan 24
21:41:18 2001
***************
*** 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.rw/src/libc/fsext/makefile Sat Nov 25 20:38:58 1995
--- /develop/djgpp.0/src/libc/fsext/makefile Wed Jan 24 22:08:44 2001
***************
*** 1,7 ****
--- 1,10 ----
+ # 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
*** /dev/null Wed Jan 24 22:13:36 2001
--- /develop/djgpp.0/tests/libc/fsext/tzero.c Wed Jan 24 21:45:40 2001
***************
*** 0 ****
--- 1,533 ----
+ /* 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);
+ }
- Raw text -