Date: Mon, 1 Sep 2003 11:09:50 -0400 (EDT) From: Dan Langille <dan@langille.org> To: FreeBSD-gnats-submit@FreeBSD.org Cc: Dan Langille <dan@langille.org> Subject: kern/56274: pthreads does not return correct value at EOT Message-ID: <20030901150950.CC8E23E5F@xeon.unixathome.org> Resent-Message-ID: <200309011510.h81FA879089216@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 56274 >Category: kern >Synopsis: pthreads does not return correct value at EOT >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Mon Sep 01 08:10:08 PDT 2003 >Closed-Date: >Last-Modified: >Originator: Dan Langille >Release: FreeBSD 4.8-STABLE i386 >Organization: >Environment: System: FreeBSD xeon.example.org 4.8-STABLE FreeBSD 4.8-STABLE #0: Fri Aug 15 16:11:30 EDT 2003 dan@polo.example.org:/usr/obj/usr/src/sys/XEON i386 Also present on FreeBSD undef.example.org 5.1-RELEASE FreeBSD 5.1-RELEASE #0: Thu Jun 5 02:55:42 GMT 2003 root@wv1u.btc.adaptec.com:/usr/obj/usr/src/sys/GENERIC i386 >Description: As taken from http://docs.freebsd.org/cgi/mid.cgi?db=irt&id=3F4CBD13.545.1FF6190E@localhost A return status of 0 from write is not interpreted as an End-Of-Tape. The threads library isn't smart enough to know that the file is a tape device and that a 0 status should break it out of the loop. Thus, it continues writing. See also: http://docs.freebsd.org/cgi/mid.cgi?db=irt&id=bilgkb$nk3$1@sea.gmane.org >How-To-Repeat: If you run this code with pthreads, you'll get a different number of records written than if you run it without pthreads. Tested under both 4.8 and 5.1. /* * * Program to test loss of data at EOM on * FreeBSD systems. * * Kern Sibbald, August 2003 * * Build this program with: * * c++ -g -O2 -Wall -c tapetest.c * c++ -g -O2 -Wall tapetest.o -o tapetest * * Procedure for testing tape * ./tapetest /dev/your-tape-device * rewind * rawwrite * rewind * scan * */ #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <stdarg.h> #include <errno.h> #include <sys/ioctl.h> #include <string.h> #include <strings.h> #include <sys/mtio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <ctype.h> #define FALSE 0 #define TRUE 1 #define dev_state(dev, state) ((dev)->state & (state)) /* Device state bits */ #define ST_OPENED (1<<0) /* set when device opened */ #define ST_TAPE (1<<1) /* is a tape device */ #define ST_FILE (1<<2) /* is a file device */ #define ST_FIFO (1<<3) /* is a fifo device */ #define ST_PROG (1<<4) /* is a program device */ #define ST_LABEL (1<<5) /* label found */ #define ST_MALLOC (1<<6) /* dev packet malloc'ed in init_dev() */ #define ST_APPEND (1<<7) /* ready for Bacula append */ #define ST_READ (1<<8) /* ready for Bacula read */ #define ST_EOT (1<<9) /* at end of tape */ #define ST_WEOT (1<<10) /* Got EOT on write */ #define ST_EOF (1<<11) /* Read EOF i.e. zero bytes */ #define ST_NEXTVOL (1<<12) /* Start writing on next volume */ #define ST_SHORT (1<<13) /* Short block read */ #define BLOCK_SIZE (512 * 126) /* Exported variables */ int quit = 0; char buf[100000]; int verbose = 0; int debug_level = 0; int fd = 0; struct DEVICE { int fd; int dev_errno; int file; int block_num; int state; char *buf; int buf_len; char *dev_name; int file_addr; }; DEVICE *dev; #define uint32_t unsigned long #define uint64_t unsigned long long /* Forward referenced subroutines */ static void do_tape_cmds(); static void helpcmd(); static void scancmd(); static void rewindcmd(); static void rawfill_cmd(); /* Static variables */ static char cmd[1000]; static void usage(); int get_cmd(char *prompt); /********************************************************************* * * Main Bacula Pool Creation Program * */ int main(int argc, char *argv[]) { int ch; while ((ch = getopt(argc, argv, "d:v?")) != -1) { switch (ch) { case 'd': /* set debug level */ debug_level = atoi(optarg); if (debug_level <= 0) { debug_level = 1; } break; case 'v': verbose++; break; case '?': default: helpcmd(); exit(0); } } argc -= optind; argv += optind; /* See if we can open a device */ if (argc == 0) { printf("No archive name specified.\n"); usage(); exit(1); } else if (argc != 1) { printf("Improper number of arguments specified.\n"); usage(); exit(1); } fd = open(argv[0], O_RDWR); if (fd < 0) { printf("Error opening %s ERR=%s\n", argv[0], strerror(errno)); exit(1); } dev = (DEVICE *)malloc(sizeof(DEVICE)); memset(dev, 0, sizeof(DEVICE)); dev->fd = fd; dev->dev_name = strdup(argv[0]); dev->buf_len = BLOCK_SIZE; dev->buf = (char *)malloc(BLOCK_SIZE); do_tape_cmds(); return 0; } int rewind_dev(DEVICE *dev) { struct mtop mt_com; if (dev->fd < 0) { dev->dev_errno = EBADF; printf("Bad call to rewind_dev. Device %s not open\n", dev->dev_name); return 0; } dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ dev->block_num = dev->file = 0; mt_com.mt_op = MTREW; mt_com.mt_count = 1; if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { dev->dev_errno = errno; printf("Rewind error on %s. ERR=%s.\n", dev->dev_name, strerror(dev->dev_errno)); return 0; } return 1; } /* * Write an end of file on the device * Returns: 0 on success * non-zero on failure */ int weof_dev(DEVICE *dev, int num) { struct mtop mt_com; int stat; if (dev->fd < 0) { dev->dev_errno = EBADF; printf("Bad call to fsf_dev. Archive not open\n"); return -1; } dev->state &= ~(ST_EOT | ST_EOF); /* remove EOF/EOT flags */ dev->block_num = 0; printf("weof_dev\n"); mt_com.mt_op = MTWEOF; mt_com.mt_count = num; stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); if (stat == 0) { dev->file++; dev->file_addr = 0; } else { dev->dev_errno = errno; printf("ioctl MTWEOF error on %s. ERR=%s.\n", dev->dev_name, strerror(dev->dev_errno)); } return stat; } void quitcmd() { quit = 1; } /* * Rewind the tape. */ static void rewindcmd() { if (!rewind_dev(dev)) { printf("Bad status from rewind. ERR=%s\n", strerror(dev->dev_errno)); } else { printf("Rewound %s\n", dev->dev_name); } } /* * Write and end of file on the tape */ static void weofcmd() { int stat; if ((stat = weof_dev(dev, 1)) < 0) { printf("Bad status from weof %d. ERR=%s\n", stat, strerror(dev->dev_errno)); return; } else { printf("Wrote EOF to %s\n", dev->dev_name); } } /* * Read a record from the tape */ static void rrcmd() { char *buf; int stat, len; if (!get_cmd("Enter length to read: ")) { return; } len = atoi(cmd); if (len < 0 || len > 1000000) { printf("Bad length entered, using default of 1024 bytes.\n"); len = 1024; } buf = (char *)malloc(len); stat = read(fd, buf, len); if (stat > 0 && stat <= len) { errno = 0; } printf("Read of %d bytes gives stat=%d. ERR=%s\n", len, stat, strerror(errno)); free(buf); } /* * Write a record to the tape */ static void wrcmd() { int stat; int rfd; rfd = open("/dev/urandom", O_RDONLY); if (rfd) { read(rfd, dev->buf, dev->buf_len); } else { printf("Cannot open /dev/urandom.\n"); return; } printf("Write one block of %u bytes.\n", dev->buf_len); stat = write(dev->fd, dev->buf, dev->buf_len); if (stat != (int)dev->buf_len) { if (stat == -1) { printf("Bad status from write. ERR=%s\n", strerror(errno)); } else { printf("Expected to write %d bytes but wrote only %d.\n", dev->buf_len, stat); } } } /* * Scan tape by reading block by block. Report what is * on the tape. Note, this command does raw reads, and as such * will not work with fixed block size devices. */ static void scancmd() { int stat; int blocks, tot_blocks, tot_files; int block_size; uint64_t bytes; blocks = block_size = tot_blocks = 0; bytes = 0; if (dev->state & ST_EOT) { printf("End of tape\n"); return; } tot_files = dev->file; printf("Starting scan at file %u\n", dev->file); for (;;) { if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) { dev->dev_errno = errno; printf("Bad status from read %d. ERR=%s\n", stat, strerror(dev->dev_errno)); if (blocks > 0) printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); return; } if (stat != block_size) { if (blocks > 0) { printf("%d block%s of %d bytes in file %d\n", blocks, blocks>1?"s":"", block_size, dev->file); blocks = 0; } block_size = stat; } if (stat == 0) { /* EOF */ printf("End of File mark.\n"); /* Two reads of zero means end of tape */ if (dev->state & ST_EOF) dev->state |= ST_EOT; else { dev->state |= ST_EOF; dev->file++; } if (dev->state & ST_EOT) { printf("End of tape\n"); break; } } else { /* Got data */ dev->state &= ~ST_EOF; blocks++; tot_blocks++; bytes += stat; } } tot_files = dev->file - tot_files; printf("Total files=%d, blocks=%d, bytes = %d\n", tot_files, tot_blocks, (int)bytes); } static void rawfill_cmd() { int stat; int rfd; uint32_t block_num = 0; uint32_t *p; int my_errno; rfd = open("/dev/urandom", O_RDONLY); if (rfd) { read(rfd, dev->buf, dev->buf_len); } else { printf("Cannot open /dev/urandom.\n"); return; } p = (uint32_t *)dev->buf; printf("Begin writing blocks of %u bytes.\n", dev->buf_len); for ( ;; ) { *p = block_num; stat = write(dev->fd, dev->buf, dev->buf_len); if (stat == (int)dev->buf_len) { if ((block_num++ % 100) == 0) { printf("+"); fflush(stdout); } continue; } break; } my_errno = errno; printf("\n"); weofcmd(); printf("Write failed. Last block written=%d. stat=%d ERR=%s\n", (int)block_num, stat, strerror(my_errno)); } /* Strip any trailing junk from the command */ void strip_trailing_junk(char *cmd) { char *p; p = cmd + strlen(cmd) - 1; /* strip trailing junk from command */ while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' ')) *p-- = 0; } /* folded search for string - case insensitive */ int fstrsch(char *a, char *b) /* folded case search */ { register char *s1,*s2; register char c1=0, c2=0; s1=a; s2=b; while (*s1) { /* do it the fast way */ if ((*s1++ | 0x20) != (*s2++ | 0x20)) return 0; /* failed */ } while (*a) { /* do it over the correct slow way */ if (isupper(c1 = *a)) { c1 = tolower((int)c1); } if (isupper(c2 = *b)) { c2 = tolower((int)c2); } if (c1 != c2) { return 0; } a++; b++; } return 1; } struct cmdstruct { char *key; void (*func)(); char *help; }; static struct cmdstruct commands[] = { {"help", helpcmd, "print this command"}, {"quit", quitcmd, "quit tapetest"}, {"rawfill", rawfill_cmd, "use write() to fill tape"}, {"rewind", rewindcmd, "rewind the tape"}, {"rr", rrcmd, "raw read the tape"}, {"wr", wrcmd, "raw write one block to the tape"}, {"scan", scancmd, "read() tape block by block to EOT and report"}, {"weof", weofcmd, "write an EOF on the tape"}, }; #define comsize (sizeof(commands)/sizeof(struct cmdstruct)) static void do_tape_cmds() { unsigned int i; int found; while (get_cmd("*")) { found = 0; for (i=0; i<comsize; i++) /* search for command */ if (fstrsch(cmd, commands[i].key)) { (*commands[i].func)(); /* go execute command */ found = 1; break; } if (!found) { printf("%s is an illegal command\n", cmd); } if (quit) { break; } } } static void helpcmd() { unsigned int i; usage(); printf(" Command Description\n ======= ===========\n"); for (i=0; i<comsize; i++) printf(" %-10s %s\n", commands[i].key, commands[i].help); printf("\n"); } static void usage() { fprintf(stderr, "Usage: tapetest [-d debug_level] [device_name]\n" " -dnn set debug level to nn\n" " -? print this message.\n" "\n"); } /* * Get next input command from terminal. This * routine is REALLY primitive, and should be enhanced * to have correct backspacing, etc. */ int get_cmd(char *prompt) { int i = 0; int ch; fprintf(stdout, prompt); /* We really should turn off echoing and pretty this * up a bit. */ cmd[i] = 0; while ((ch = fgetc(stdin)) != EOF) { if (ch == '\n') { strip_trailing_junk(cmd); return 1; } else if (ch == 4 || ch == 0xd3 || ch == 0x8) { if (i > 0) cmd[--i] = 0; continue; } cmd[i++] = ch; cmd[i] = 0; } quit = 1; return 0; } >Fix: See http://docs.freebsd.org/cgi/mid.cgi?db=irt&id=bilgkb$nk3$1@sea.gmane.org >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030901150950.CC8E23E5F>