Whamcloud - gitweb
LU-17402 kernel: update dotdot patch path for RHEL 8.10
[fs/lustre-release.git] / lustre / tests / fsx.c
index 2f765c0..2a043d3 100644 (file)
@@ -75,6 +75,9 @@
 #include <libcfs/util/string.h>
 #include <setjmp.h>
 
+#include <linux/lustre/lustre_idl.h>
+#include <lustre/lustreapi.h>
+
 /*
  * Each test run will work with one or more separate file descriptors for the
  * same file.  This allows testing cache coherency across multiple mountpoints
@@ -116,6 +119,7 @@ int logcount; /* total ops */
 int jmpbuf_good;
 jmp_buf jmpbuf;
 
+unsigned int mirror_ids[LUSTRE_MIRROR_COUNT_MAX];
 /*
  * Define operations
  */
@@ -133,11 +137,22 @@ jmp_buf jmpbuf;
 #define OP_PUNCH_HOLE          6
 #define OP_ZERO_RANGE          7
 #define OP_CLOSEOPEN           8
-#define OP_MAX_FULL            9
+#define OP_MIRROR_OPS          9
+#define OP_MAX_FULL            10
+
+#define MIRROR_EXTEND 0
+#define MIRROR_SPLIT 1
+#define MIRROR_RESYNC 2
+#define MIRROR_OPS 3
+
+char *mirror_op_str[] = {
+       [MIRROR_EXTEND] = "MIRROR_EXTEND",
+       [MIRROR_SPLIT]  = "MIRROR_SPLIT",
+       [MIRROR_RESYNC] = "MIRROR_RESYNC",
+};
 
 #define OP_SKIPPED 101
-/* _GNU_SOURCE defines O_DIRECT as 14th bit which is 0x4000(16384) */
-#define OP_DIRECT  16384
+#define OP_DIRECT O_DIRECT
 
 #ifndef FALLOC_FL_PUNCH_HOLE
 #define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */
@@ -182,6 +197,7 @@ int truncbdy = 1;                   /* -t flag */
 int writebdy = 1;                      /* -w flag */
 long monitorstart = -1;                        /* -m flag */
 long monitorend = -1;                  /* -m flag */
+long flrmode;                          /* -M flag */
 int lite;                              /* -L flag */
 long numops = -1;                      /* -N flag */
 int randomoplen = 1;                   /* -O flag disables it */
@@ -201,7 +217,7 @@ int page_mask;
 FILE *fsxlogf;
 int badoff = -1;
 
-void
+static void
 vwarnc(code, fmt, ap)
        int code;
        const char *fmt;
@@ -215,7 +231,7 @@ vwarnc(code, fmt, ap)
        fprintf(stderr, "%s\n", strerror(code));
 }
 
-void
+static void
 __attribute__((format(__printf__, 1, 2)))
 warn(const char *fmt, ...)
 {
@@ -226,7 +242,7 @@ warn(const char *fmt, ...)
        va_end(ap);
 }
 
-void
+static void
 __attribute__((format(__printf__, 1, 2)))
 prt(char *fmt, ...)
 {
@@ -248,14 +264,14 @@ prt(char *fmt, ...)
  * which transparently handles passing of function name.
  * This version also keeps checkpatch happy.
  */
-void
+static void
 ptrerr_func(const char *func, const char *prefix)
 {
        prt("%s: %s%s%s\n", func, prefix, prefix ? ": " : "", strerror(errno));
 }
 #define prterr(prefix) ptrerr_func(__func__, prefix)
 
-void
+static void
 log4(int operation, int arg0, int arg1, int arg2)
 {
        struct log_entry *le;
@@ -274,7 +290,7 @@ log4(int operation, int arg0, int arg1, int arg2)
                logptr = 0;
 }
 
-const char *
+static const char *
 fill_tf_buf(const struct test_file *tf)
 {
        static int max_tf_len;
@@ -293,7 +309,7 @@ fill_tf_buf(const struct test_file *tf)
        return tf_buf;
 }
 
-void
+static void
 logdump(void)
 {
        int i, count, down;
@@ -359,15 +375,15 @@ logdump(void)
                        break;
                case OP_TRUNCATE:
                        down = lp->args[0] < lp->args[1];
-                       prt("TRUNCATE %s\tfrom 0x%05x to 0x%05x",
-                           down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
+                       prt("TRUNC%s 0x%05x to 0x%05x",
+                           down ? "_DN" : "_UP", lp->args[1], lp->args[0]);
                        if (badoff >= lp->args[!down] &&
                            badoff < lp->args[!!down])
-                               prt("\t******WWWW");
+                               prt("\t******TTTT");
                        break;
                case OP_FALLOCATE:
                        /* 0: offset 1: length 2: where alloced */
-                       prt("FALLOC  \tfrom 0x%05x to 0x%05x\t(0x%05x bytes)%s",
+                       prt("FALLOC   0x%05x thru 0x%05x\t(0x%05x bytes)%s",
                            lp->args[0], lp->args[0] + lp->args[1],
                            lp->args[1], falloc_type[lp->args[2]]);
                        if (badoff >= lp->args[0] &&
@@ -395,6 +411,17 @@ logdump(void)
                        prt("CLOSE/OPEN%s",
                            lp->operation & OP_DIRECT ? "_OD" : "   ");
                        break;
+               case OP_MIRROR_OPS: {
+                       prt("%s ", mirror_op_str[lp->args[0]]);
+                       if (lp->args[0] == MIRROR_EXTEND)
+                               prt("to %d mirrors", lp->args[1] + 1);
+                       else if (lp->args[0] == MIRROR_SPLIT)
+                               prt("mirror %d to %d mirrors", lp->args[2],
+                                   lp->args[1] - 1);
+                       else if (lp->args[0] == MIRROR_RESYNC)
+                               prt("%d mirrors", lp->args[1]);
+                       break;
+               }
                case OP_SKIPPED:
                        prt("SKIPPED (no operation)");
                        break;
@@ -409,7 +436,7 @@ logdump(void)
        }
 }
 
-void
+static void
 save_buffer(char *buffer, off_t bufferlength, int fd)
 {
        off_t ret;
@@ -450,10 +477,11 @@ save_buffer(char *buffer, off_t bufferlength, int fd)
        }
 }
 
-void
+static void
 report_failure(int status)
 {
        logdump();
+       prt("Using seed %d\n", seed);
 
        if (fsxgoodfd) {
                if (good_buf) {
@@ -470,7 +498,7 @@ report_failure(int status)
 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
                      *(((unsigned char *)(cp)) + 1)))
 
-void
+static void
 check_buffers(unsigned int offset, unsigned int size)
 {
        unsigned char c, t;
@@ -514,7 +542,7 @@ check_buffers(unsigned int offset, unsigned int size)
        }
 }
 
-struct test_file *
+static struct test_file *
 get_tf(void)
 {
        unsigned int index = 0;
@@ -537,7 +565,7 @@ get_tf(void)
        return &test_files[index % num_test_files];
 }
 
-void
+static void
 assign_fd_policy(char *policy)
 {
        if (!strcmp(policy, "random")) {
@@ -550,7 +578,7 @@ assign_fd_policy(char *policy)
        }
 }
 
-int
+static int
 get_fd(void)
 {
        struct test_file *tf = get_tf();
@@ -565,7 +593,7 @@ static const char *my_basename(const char *path)
        return c ? c++ : path;
 }
 
-void
+static void
 open_test_files(char **argv, int argc)
 {
        struct test_file *tf;
@@ -583,9 +611,7 @@ open_test_files(char **argv, int argc)
 
        for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
                tf->path = argv[i];
-#ifdef O_DIRECT
                tf->o_direct = (random() % (o_direct + 1)) ? OP_DIRECT : 0;
-#endif
                tf->fd = open(tf->path,
                              O_RDWR | (lite ? 0 : O_CREAT | O_TRUNC) |
                              tf->o_direct, 0666);
@@ -602,7 +628,7 @@ open_test_files(char **argv, int argc)
                prt("fd %d: %s\n", i, tf->path);
 }
 
-void
+static void
 close_test_files(void)
 {
        int i;
@@ -616,7 +642,7 @@ close_test_files(void)
        }
 }
 
-void
+static void
 check_size(void)
 {
        struct stat statbuf;
@@ -637,7 +663,7 @@ check_size(void)
        }
 }
 
-void
+static void
 check_trunc_hack(void)
 {
        struct stat statbuf;
@@ -666,7 +692,7 @@ check_trunc_hack(void)
        }
 }
 
-void
+static void
 output_line(struct test_file *tf, int op, unsigned int offset,
            unsigned int size)
 {
@@ -679,6 +705,7 @@ output_line(struct test_file *tf, int op, unsigned int offset,
                [OP_READ + OP_DIRECT] = "read_OD",
                [OP_WRITE + OP_DIRECT] = "write_OD",
                [OP_FALLOCATE] = "fallocate",
+               [OP_PUNCH_HOLE] = "punch from",
        };
 
        /* W. */
@@ -689,13 +716,39 @@ output_line(struct test_file *tf, int op, unsigned int offset,
            (monitorend == -1 || offset <= monitorend)))))))
                return;
 
-       prt("%06lu%s %lu.%06u %-10s %#08x %s %#08x\t(0x05%x bytes)\n",
+       prt("%06lu%s %lu.%06u %-10s %#08x %s %#08x\t(0x%x bytes)\n",
            testcalls, fill_tf_buf(tf), tv.tv_sec, (int)tv.tv_usec,
-           ops[op], offset, op == OP_TRUNCATE ? " to " : "thru",
-           offset + size - 1, (int)size < 0 ? -(int)size : size);
+           ops[op], offset, op == OP_TRUNCATE || op == OP_PUNCH_HOLE ?
+           " to " : "thru", offset + size - 1,
+            (int)size < 0 ? -(int)size : size);
+}
+
+static void
+mirror_output_line(struct test_file *tf, int op, int mirrors, int id)
+{
+       if (!(!quiet &&
+             ((progressinterval && testcalls % progressinterval == 0) ||
+              (debug && (monitorstart == -1)))))
+               return;
+
+       prt("%06lu %lu.%06u %-10s ",
+           testcalls, tv.tv_sec, (int)tv.tv_usec, mirror_op_str[op]);
+
+       switch (op) {
+       case MIRROR_EXTEND:
+               prt("to %d mirrors\n", mirrors + 1);
+               break;
+       case MIRROR_SPLIT:
+               prt("mirror %d to %d mirrors\n", id, mirrors - 1);
+               break;
+       case MIRROR_RESYNC:
+               prt("%d mirrors\n", mirrors);
+               break;
+       }
 }
 
-void output_debug(unsigned int offset, unsigned int size, const char *what)
+static void output_debug(unsigned int offset, unsigned int size,
+                        const char *what)
 {
        struct timeval t;
 
@@ -707,7 +760,7 @@ void output_debug(unsigned int offset, unsigned int size, const char *what)
        }
 }
 
-void
+static void
 doflush(unsigned int offset, unsigned int size)
 {
        unsigned int pg_offset;
@@ -740,7 +793,7 @@ doflush(unsigned int offset, unsigned int size)
        output_debug(offset, size, "flush done");
 }
 
-void
+static void
 doread(unsigned int offset, unsigned int size)
 {
        off_t ret;
@@ -790,7 +843,7 @@ doread(unsigned int offset, unsigned int size)
        check_buffers(offset, size);
 }
 
-void
+static void
 check_eofpage(char *s, unsigned int offset, char *p, int size)
 {
        long last_page, should_be_zero;
@@ -817,7 +870,7 @@ check_eofpage(char *s, unsigned int offset, char *p, int size)
                }
 }
 
-void
+static void
 domapread(unsigned int offset, unsigned int size)
 {
        unsigned int pg_offset;
@@ -876,7 +929,7 @@ domapread(unsigned int offset, unsigned int size)
        check_buffers(offset, size);
 }
 
-void
+static void
 gendata(char *original_buf, char *good_buf, unsigned int offset,
        unsigned int size)
 {
@@ -888,7 +941,7 @@ gendata(char *original_buf, char *good_buf, unsigned int offset,
        }
 }
 
-void
+static void
 dowrite(unsigned int offset, unsigned int size)
 {
        off_t ret;
@@ -953,7 +1006,7 @@ dowrite(unsigned int offset, unsigned int size)
        }
 }
 
-void
+static void
 domapwrite(unsigned int offset, unsigned int size)
 {
        unsigned int pg_offset;
@@ -1028,7 +1081,7 @@ domapwrite(unsigned int offset, unsigned int size)
        output_debug(offset, map_size, "munmap done");
 }
 
-void
+static void
 dotruncate(unsigned int size)
 {
        int oldsize = file_size;
@@ -1062,7 +1115,7 @@ dotruncate(unsigned int size)
        output_debug(size, 0, "truncate done");
 }
 
-void
+static void
 do_punch_hole(unsigned int offset, unsigned int length)
 {
        int max_offset = 0;
@@ -1107,7 +1160,7 @@ do_punch_hole(unsigned int offset, unsigned int length)
        memset(good_buf + max_offset, '\0', max_len);
 }
 
-void
+static void
 do_zero_range(unsigned int offset, unsigned int length)
 {
        unsigned int end_offset;
@@ -1163,11 +1216,10 @@ do_zero_range(unsigned int offset, unsigned int length)
  * fallocate is basically a no-op unless extending,
  * then a lot like a truncate
  */
-void
+static void
 do_preallocate(unsigned int offset, unsigned int length)
 {
        off_t end_offset;
-       off_t new_offset;
        int keep_size;
        int fd;
        struct stat statbufs;
@@ -1183,8 +1235,7 @@ do_preallocate(unsigned int offset, unsigned int length)
 
        keep_size = fl_keep_size && (random() % 2);
 
-       end_offset = keep_size ? 0 : offset + length;
-
+       end_offset = offset + length;
        if (end_offset > biggest) {
                biggest = end_offset;
                if (!quiet && testcalls > simulatedopcount)
@@ -1200,12 +1251,9 @@ do_preallocate(unsigned int offset, unsigned int length)
        log4(OP_FALLOCATE, offset, length, (end_offset > file_size) ?
             (keep_size ? 0 : 1) : 2);
 
-       if (end_offset > file_size) {
+       if (end_offset > file_size && !keep_size) {
                memset(good_buf + file_size, '\0', end_offset - file_size);
                file_size = end_offset;
-       } else {
-               new_offset = file_size - (offset + length);
-               length = length + new_offset;
        }
 
        if (testcalls <= simulatedopcount)
@@ -1222,7 +1270,7 @@ do_preallocate(unsigned int offset, unsigned int length)
        output_debug(offset, length, "fallocate done");
 }
 
-void
+static void
 writefileimage()
 {
        ssize_t iret;
@@ -1248,7 +1296,7 @@ writefileimage()
        }
 }
 
-void
+static void
 docloseopen(void)
 {
        int direct = 0;
@@ -1258,9 +1306,7 @@ docloseopen(void)
                return;
 
        tf = get_tf();
-#ifdef O_DIRECT
        direct = (random() % (o_direct + 1)) ? OP_DIRECT : 0;
-#endif
        log4(OP_CLOSEOPEN + direct, file_size, (unsigned int)file_size, 0);
 
        if (fd_policy != FD_SINGLE)
@@ -1283,6 +1329,166 @@ docloseopen(void)
                     tf->o_direct ? "open(O_DIRECT) done" : "open done");
 }
 
+static int
+get_mirror_ids(int fd, unsigned int *ids)
+{
+       struct llapi_layout *layout;
+       uint16_t count;
+       int rc;
+
+       layout = llapi_layout_get_by_fd(fd, 0);
+       if (layout == NULL)
+               return 0;
+
+       /* only get mirror count */
+       rc = llapi_layout_mirror_count_get(layout, &count);
+       if (rc < 0)
+               prt("llapi_layout_mirror_count_get: %d\n", rc);
+       if (count == 0)
+               return 0;
+
+       count = 0;
+
+       rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
+       if (rc < 0) {
+               prt("llapi_layout_comp_use(USE_FIRST): %d\n", rc);
+               goto free;
+       }
+
+       do {
+               unsigned int id;
+
+               rc = llapi_layout_mirror_id_get(layout, &id);
+               if (rc < 0) {
+                       prt("llapi_layout_mirror_id_get: %d\n", rc);
+                       goto free;
+               }
+
+               if (!count || ids[count - 1] != id)
+                       ids[count++] = id;
+
+               rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_NEXT);
+               if (rc < 0) {
+                       prt("llapi_layout_comp_use(USE_NEXT): %d\n", rc);
+                       goto free;
+               }
+       } while (rc == 0);
+
+free:
+       llapi_layout_free(layout);
+
+       return rc < 0 ? rc : count;
+}
+
+static void
+do_mirror_ops(int op)
+{
+       int mirror_count;
+       char cmd[PATH_MAX * 2];
+       int i = 0;
+       int rc;
+
+       if (testcalls <= simulatedopcount)
+               return;
+
+       tf = get_tf();
+
+       mirror_count = get_mirror_ids(tf->fd, mirror_ids);
+       if (mirror_count < 0) {
+               prterr("get_mirror_ids");
+               report_failure(182);
+       }
+
+       switch (op) {
+       case MIRROR_EXTEND:
+               if (mirror_count == LUSTRE_MIRROR_COUNT_MAX)
+                       return;
+               snprintf(cmd, sizeof(cmd), "lfs mirror extend -N -c-1 %s",
+                        tf->path);
+               break;
+       case MIRROR_SPLIT:
+               if (mirror_count == 0 || mirror_count == 1)
+                       return;
+
+               i = random() % mirror_count;
+               if (i == 0)
+                       i++;
+
+               snprintf(cmd, sizeof(cmd),
+                        "lfs mirror split -d --mirror-id=%d %s",
+                        mirror_ids[i], tf->path);
+               break;
+       case MIRROR_RESYNC:
+               if (mirror_count < 2)
+                       return;
+
+               snprintf(cmd, sizeof(cmd),
+                        "lfs mirror resync %s", tf->path);
+               break;
+       }
+
+       if (close(tf->fd))
+               report_failure(183);
+       output_debug(monitorstart, 0, "close done");
+
+       log4(OP_MIRROR_OPS, op, mirror_count, i);
+
+       mirror_output_line(tf, op, mirror_count, i);
+
+       rc = system(cmd);
+       if (rc < 0) {
+               prt("%s: %d\n", cmd, errno);
+               report_failure(184);
+       } else if (WIFEXITED(rc)) {
+               rc = WEXITSTATUS(rc);
+               if (rc > 0) {
+                       prt("%s: %d\n", cmd, rc);
+                       /* mirror split won't delete the last non-stale mirror,
+                        * and returns EUCLEAN
+                        */
+                       if (rc != EUCLEAN)
+                               report_failure(184);
+               }
+       }
+       output_debug(monitorstart, 0, cmd);
+
+       switch (op) {
+       case MIRROR_SPLIT:
+               if (mirror_count == 2)
+                       break;
+       case MIRROR_EXTEND:
+       case MIRROR_RESYNC:
+               /* verify mirror */
+               snprintf(cmd, sizeof(cmd),
+                        "lfs mirror verify %s", tf->path);
+
+               rc = system(cmd);
+               if (rc < 0) {
+                       prt("mirror op %d: %s: %d\n", op, cmd, errno);
+                       report_failure(184);
+               } else if (WIFEXITED(rc)) {
+                       rc = WEXITSTATUS(rc);
+                       if (rc > 0) {
+                               prt("mirror op %d: %s: %d\n", op, cmd, rc);
+                               snprintf(cmd, sizeof(cmd),
+                                        "lfs mirror verify -v %s", tf->path);
+                               rc = system(cmd);
+                               report_failure(184);
+                       }
+               }
+       }
+
+       output_debug(monitorstart, 0, cmd);
+
+       tf->fd = open(tf->path, O_RDWR | tf->o_direct, 0);
+       if (tf->fd < 0) {
+               prterr(tf->o_direct ? "open(O_DIRECT)" : "open");
+               report_failure(185);
+       }
+       output_debug(monitorstart, 0,
+                    tf->o_direct ? "open(O_DIRECT) done" : "open done");
+}
+
 #define TRIM_OFF_LEN(off, len, size)   \
 do {                                   \
        if (size)                       \
@@ -1293,7 +1499,7 @@ do {                                      \
                (len) = (size) - (off); \
 } while (0)
 
-void
+static void
 test(void)
 {
        unsigned long offset;
@@ -1393,6 +1599,10 @@ test(void)
                if (closeopen)
                        docloseopen();
                break;
+       case OP_MIRROR_OPS:
+               if (flrmode)
+                       do_mirror_ops(random() % MIRROR_OPS);
+               break;
        default:
                prterr("unknown operation %d: Operation not supported");
                report_failure(42);
@@ -1404,7 +1614,7 @@ out:
                check_size();
 }
 
-void
+static void
 segv(int sig)
 {
        if (jmpbuf_good) {
@@ -1414,7 +1624,7 @@ segv(int sig)
        report_failure(9999);
 }
 
-void
+static void
 cleanup(sig)
        int     sig;
 {
@@ -1424,7 +1634,7 @@ cleanup(sig)
        exit(sig);
 }
 
-void
+static void
 usage(void)
 {
        fprintf(stdout,
@@ -1470,7 +1680,7 @@ usage(void)
 "          order with 'rotate' or chose them at 'random'.  (default random)\n"
 "      -L: fsxLite - no file creations & no file size changes\n"
 /* OSX: -I: start interactive mode since operation opnum\n\ */
-/* OSX: -M: slow motion mode, wait 1 second before each op\n\ */
+"      -M: mirror file test mode\n"
 "      -N numops: total # operations to do (default infinity)\n"
 "      -O: use oplen (see -o flag) for every op (default random)\n"
 "      -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
@@ -1478,15 +1688,13 @@ usage(void)
 "      -S seed: for random # generator (default 1) 0 gets timestamp\n"
 /* OSX: -T datasize: atomic data element write size [1,2,4] (default 4)\n\ */
 "      -W: mapped write operations DISabled\n"
-#ifdef O_DIRECT
 "      -Z[P]: O_DIRECT file IO [1 in P chance for each open] (default off)\n"
-#endif
 "      fname: this filename is REQUIRED (no default)\n",
        page_size);
        exit(90);
 }
 
-int
+static int
 getnum(char *s, char **e)
 {
        int ret = -1;
@@ -1519,14 +1727,15 @@ getnum(char *s, char **e)
        return (ret);
 }
 
-int
+static int
 test_fallocate(int mode)
 {
        int ret = 0;
        int fd = get_fd();
 
        if (!lite) {
-               if (fallocate(fd, mode, 0, 1) && errno == EOPNOTSUPP) {
+               /* Must go more than a page away so let's go 4M to be sure */
+               if (fallocate(fd, mode, 0, 4096*1024) && errno == EOPNOTSUPP) {
                        if (!quiet)
                                warn("%s: filesystem does not support fallocate mode 0x%x, disabling!",
                                     __func__, mode);
@@ -1534,8 +1743,10 @@ test_fallocate(int mode)
                        ret = 1;
                }
 
-               /* Call truncate only when fallocate succeeds */
-               if (ret == 1 && ftruncate(fd, 0) == -1)
+               /* Always call ftruncate since file size might be adjusted
+                * by fallocate even on error
+                */
+               if (ftruncate(fd, 0) == -1)
                        warn("ftruncate to 0 size failed");
        }
        return ret;
@@ -1557,7 +1768,7 @@ main(int argc, char **argv)
        setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
 
        while ((ch = getopt(argc, argv,
-                           "b:c:dfl:m:no:p:qr:s:t:w:xyzD:FHI:LN:OP:RS:WZ::"))
+                           "b:c:dfl:m:no:p:qr:s:t:w:xyzD:FHI:LMN:OP:RS:WZ::"))
               != EOF)
                switch (ch) {
                case 'b':
@@ -1660,6 +1871,9 @@ main(int argc, char **argv)
                case 'L':
                        lite = 1;
                        break;
+               case 'M':
+                       flrmode = 1;
+                       break;
                case 'N':
                        numops = getnum(optarg, &endp);
                        if (numops < 0)
@@ -1693,12 +1907,10 @@ main(int argc, char **argv)
                                fprintf(stdout, "mapped writes DISABLED\n");
                        break;
                case 'Z':
-#ifdef O_DIRECT
                        if (optarg)
                                o_direct = getnum(optarg, &endp);
                        if (!optarg || o_direct == 0)
                                o_direct = 1;
-#endif
                        break;
                default:
                        usage();