Sender: rich AT phekda DOT freeserve DOT co DOT uk Message-ID: <3A6F5559.A187C056@phekda.freeserve.co.uk> Date: Wed, 24 Jan 2001 22:21:13 +0000 From: Richard Dawe X-Mailer: Mozilla 4.51 [en] (X11; I; Linux 2.2.17 i586) X-Accept-Language: de,fr MIME-Version: 1.0 To: DJGPP workers Subject: Latest /dev/zero and /dev/full patch Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Reply-To: djgpp-workers AT delorie DOT com 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 + #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); + } + + /* ---------------------- + * - __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 + + 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.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 */ + + #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); + }