Whamcloud - gitweb
LU-6142 tests: Fix style issues for mmap_sanity.c
[fs/lustre-release.git] / lustre / tests / multiop.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2012, 2017, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  */
32
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE /* pull in O_DIRECTORY in bits/fcntl.h */
35 #endif
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <malloc.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/mman.h>
45 #include <sys/vfs.h>
46 #include <sys/ioctl.h>
47 #include <sys/xattr.h>
48 #include <sys/file.h>
49 #include <signal.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <semaphore.h>
53 #include <time.h>
54 #include <err.h>
55
56 #include <lustre/lustreapi.h>
57
58 #define T1 "write data before unlink\n"
59 #define T2 "write data after unlink\n"
60 char msg[] = "yabba dabba doo, I'm coming for you, I live in a shoe, I don't know what to do.\n'Bigger, bigger,and bigger yet!' cried the Creator.  'You are not yet substantial enough for my boundless intents!'  And ever greater and greater the object became, until all was lost 'neath its momentus bulk.\n";
61 char *buf, *buf_align;
62 int bufsize;
63 sem_t sem;
64 #define ALIGN_LEN 65535
65 #define XATTR "user.multiop"
66
67 char usage[] =
68 "Usage: %s filename command-sequence [path...]\n"
69 "    command-sequence items:\n"
70 "        A  fsetxattr(\"user.multiop\")\n"
71 "        a[num] fgetxattr(\"user.multiop\") [optional buffer size, default 0]\n"
72 "        c  close\n"
73 "        B[num] call setstripe ioctl to create stripes\n"
74 "        C[num] create with optional stripes\n"
75 "        d  mkdir\n"
76 "        D  open(O_DIRECTORY)\n"
77 "        e[R|W|U] apply lease. R: Read; W: Write; U: Unlock\n"
78 "        E[+|-] get lease. +/-: expect lease to (not) exist\n"
79 "        f  statfs\n"
80 "        F  print FID\n"
81 "        H[num] create HSM released file with num stripes\n"
82 "        G gid get grouplock\n"
83 "        g gid put grouplock\n"
84 "        K  link path to filename\n"
85 "        L  link\n"
86 "        l  symlink filename to path\n"
87 "        m  mknod\n"
88 "        M  rw mmap to EOF (must open and stat prior)\n"
89 "        n  rename path to filename\n"
90 "        N  rename filename to path\n"
91 "        o  open(O_RDONLY)\n"
92 "        O  open(O_CREAT|O_RDWR)\n"
93 "        p  print return value of last command\n"
94 "        r[num] read [optional length]\n"
95 "        R  reference entire mmap-ed region\n"
96 "        s  stat\n"
97 "        S  fstat\n"
98 "        t  fchmod\n"
99 "        T[num] ftruncate [optional position, default 0]\n"
100 "        u  unlink\n"
101 "        U  munmap\n"
102 "        v  verbose\n"
103 "        V  open a volatile file\n"
104 "        w[num] write optional length\n"
105 "        x  get file data version\n"
106 "        W  write entire mmap-ed region\n"
107 "        y  fsync\n"
108 "        Y  fdatasync\n"
109 "        z[num] lseek(SEEK_SET) [optional offset, default 0]\n"
110 "        Z[num] lseek(SEEK_CUR) [optional offset, default 0]\n"
111 "        _  wait for signal\n";
112
113 void usr1_handler(int unused)
114 {
115         int saved_errno = errno;
116
117         /*
118          * signal(7): POSIX.1-2004 ...requires an implementation to guarantee
119          * that the following functions can be safely called inside a signal
120          * handler:
121          *            sem_post()
122          */
123         sem_post(&sem);
124
125         errno = saved_errno;
126 }
127
128 static const char *
129 pop_arg(int argc, char *argv[])
130 {
131         static int cur_arg = 3;
132
133         if (cur_arg >= argc)
134                 return NULL;
135
136         return argv[cur_arg++];
137 }
138
139 struct flag_mapping {
140         const char *string;
141         const int  flag;
142 } flag_table[] = {
143         {"O_RDONLY", O_RDONLY},
144         {"O_WRONLY", O_WRONLY},
145         {"O_RDWR", O_RDWR},
146         {"O_CREAT", O_CREAT},
147         {"O_EXCL", O_EXCL},
148         {"O_NOCTTY", O_NOCTTY},
149         {"O_TRUNC", O_TRUNC},
150         {"O_APPEND", O_APPEND},
151         {"O_NONBLOCK", O_NONBLOCK},
152         {"O_NDELAY", O_NDELAY},
153         {"O_SYNC", O_SYNC},
154 #ifdef O_DIRECT
155         {"O_DIRECT", O_DIRECT},
156 #endif
157 #ifdef O_NOATIME
158         {"O_NOATIME", O_NOATIME},
159 #endif
160         {"O_LARGEFILE", O_LARGEFILE},
161         {"O_DIRECTORY", O_DIRECTORY},
162         {"O_NOFOLLOW", O_NOFOLLOW},
163         {"O_LOV_DELAY_CREATE", O_LOV_DELAY_CREATE},
164         {"", -1}
165 };
166
167 int get_flags(char *data, int *rflags)
168 {
169         char *cloned_flags;
170         char *tmp;
171         int flag_set = 0;
172         int flags = 0;
173         int size = 0;
174
175         cloned_flags = strdup(data);
176         if (!cloned_flags) {
177                 fprintf(stderr, "Insufficient memory.\n");
178                 exit(-1);
179         }
180
181         for (tmp = strtok(cloned_flags, ":"); tmp;
182              tmp = strtok(NULL, ":")) {
183                 int i;
184
185                 size = tmp - cloned_flags;
186                 for (i = 0; flag_table[i].flag != -1; i++) {
187                         if (!strcmp(tmp, flag_table[i].string)) {
188                                 flags |= flag_table[i].flag;
189                                 size += strlen(flag_table[i].string);
190                                 flag_set = 1;
191                                 break;
192                         }
193                 }
194         }
195         free(cloned_flags);
196
197         if (!flag_set) {
198                 *rflags = O_RDONLY;
199                 return 0;
200         }
201
202         *rflags = flags;
203         return size;
204 }
205
206 #define POP_ARG() (pop_arg(argc, argv))
207
208 int main(int argc, char **argv)
209 {
210         char *fname, *commands;
211         const char *newfile;
212         const char *oldpath;
213         struct stat st;
214         struct statfs stfs;
215         size_t mmap_len = 0, i;
216         unsigned char *mmap_ptr = NULL, junk = 1;
217         int len, fd = -1;
218         int flags;
219         int save_errno;
220         int verbose = 0;
221         int gid = 0;
222         struct lu_fid fid;
223         struct timespec ts;
224         struct lov_user_md_v3 lum;
225         char *xattr_buf = NULL;
226         size_t xattr_buf_size = 0;
227         long long rc = 0;
228         long long last_rc;
229
230         if (argc < 3) {
231                 fprintf(stderr, usage, argv[0]);
232                 exit(1);
233         }
234
235         memset(&st, 0, sizeof(st));
236         sem_init(&sem, 0, 0);
237         /* use sigaction instead of signal to avoid SA_ONESHOT semantics */
238         sigaction(SIGUSR1,
239                   &(const struct sigaction){.sa_handler = &usr1_handler}, NULL);
240
241         fname = argv[1];
242
243         for (commands = argv[2]; *commands; commands++) {
244                 /*
245                  * XXX Most commands return 0 or we exit so we only
246                  * update rc where really needed.
247                  */
248                 last_rc = rc;
249                 rc = 0;
250
251                 switch (*commands) {
252                 case '_':
253                         if (verbose) {
254                                 printf("PAUSING\n");
255                                 fflush(stdout);
256                         }
257                         len = atoi(commands + 1);
258                         if (len <= 0)
259                                 len = 3600; /* 1 hour */
260                         ts.tv_sec = time(NULL) + len;
261                         ts.tv_nsec = 0;
262                         while (sem_timedwait(&sem, &ts) < 0 && errno == EINTR)
263                                 ;
264                         break;
265                 case 'A':
266                         if (fsetxattr(fd, XATTR, "multiop", 8, 0)) {
267                                 save_errno = errno;
268                                 perror("fsetxattr");
269                                 exit(save_errno);
270                         }
271                         break;
272                 case 'a':
273                         len = atoi(commands + 1);
274                         if (xattr_buf_size < len) {
275                                 xattr_buf = realloc(xattr_buf, len);
276                                 if (!xattr_buf) {
277                                         save_errno = errno;
278                                         perror("allocating xattr buffer\n");
279                                         exit(save_errno);
280                                 }
281
282                                 xattr_buf_size = len;
283                         }
284
285                         rc = fgetxattr(fd, XATTR, xattr_buf, len);
286                         if (rc < 0) {
287                                 save_errno = errno;
288                                 perror("fgetxattr");
289                                 exit(save_errno);
290                         }
291                         break;
292                 case 'c':
293                         if (close(fd) == -1) {
294                                 save_errno = errno;
295                                 perror("close");
296                                 exit(save_errno);
297                         }
298                         fd = -1;
299                         break;
300                 case 'B':
301                         lum = (struct lov_user_md_v3) {
302                                 .lmm_magic = LOV_USER_MAGIC_V3,
303                                 .lmm_stripe_count = atoi(commands + 1),
304                         };
305
306                         if (ioctl(fd, LL_IOC_LOV_SETSTRIPE, &lum) < 0) {
307                                 save_errno = errno;
308                                 perror("LL_IOC_LOV_SETSTRIPE");
309                                 exit(save_errno);
310                         }
311                         break;
312                 case 'C':
313                         len = atoi(commands + 1);
314                         fd = llapi_file_open(fname, O_CREAT | O_WRONLY, 0644,
315                                              0, 0, len, 0);
316                         if (fd == -1) {
317                                 save_errno = errno;
318                                 perror("create stripe file");
319                                 exit(save_errno);
320                         }
321                         rc = fd;
322                         break;
323                 case 'd':
324                         if (mkdir(fname, 0755) == -1) {
325                                 save_errno = errno;
326                                 perror("mkdir(0755)");
327                                 exit(save_errno);
328                         }
329                         break;
330                 case 'D':
331                         fd = open(fname, O_DIRECTORY);
332                         if (fd == -1) {
333                                 save_errno = errno;
334                                 perror("open(O_DIRECTORY)");
335                                 exit(save_errno);
336                         }
337                         rc = fd;
338                         break;
339                 case 'e':
340                         commands++;
341                         switch (*commands) {
342                         case 'U':
343                                 rc = llapi_lease_release(fd);
344                                 break;
345                         case 'R':
346                                 rc = llapi_lease_acquire(fd, LL_LEASE_RDLCK);
347                                 break;
348                         case 'W':
349                                 rc = llapi_lease_acquire(fd, LL_LEASE_WRLCK);
350                                 break;
351                         default:
352                                 errx(-1, "unknown mode: %c", *commands);
353                         }
354                         if (rc < 0)
355                                 err(errno, "apply/unlock lease error");
356
357                         if (flags != LL_LEASE_UNLCK)
358                                 break;
359
360                         /* F_UNLCK, interpret return code */
361                         if (rc > 0) {
362                                 const char *str = "unknown";
363
364                                 if (rc == LL_LEASE_RDLCK)
365                                         str = "read";
366                                 else if (rc == LL_LEASE_WRLCK)
367                                         str = "write";
368                                 fprintf(stdout, "%s lease(%lld) released.\n",
369                                         str, rc);
370                         } else if (rc == 0) {
371                                 fprintf(stdout, "lease already broken.\n");
372                         }
373                         break;
374                 case 'E':
375                         commands++;
376                         if (*commands != '-' && *commands != '+')
377                                 errx(-1, "unknown mode: %c\n", *commands);
378
379                         rc = llapi_lease_check(fd);
380                         if (rc > 0) {
381                                 const char *str = "unknown";
382
383                                 if (rc == LL_LEASE_RDLCK)
384                                         str = "read";
385                                 else if (rc == LL_LEASE_WRLCK)
386                                         str = "write";
387                                 fprintf(stdout, "%s lease(%lld) has applied.\n",
388                                         str, rc);
389                                 if (*commands == '-')
390                                         errx(-1, "expect lease to not exist");
391                         } else if (rc == 0) {
392                                 fprintf(stdout, "no lease applied.\n");
393                                 if (*commands == '+')
394                                         errx(-1, "expect lease exists");
395                         } else {
396                                 err(errno, "free lease error");
397                         }
398                         break;
399                 case 'f':
400                         if (statfs(fname, &stfs) == -1)
401                                 errx(-1, "statfs()");
402                         break;
403                 case 'F':
404                         if (fd == -1)
405                                 rc = llapi_path2fid(fname, &fid);
406                         else
407                                 rc = llapi_fd2fid(fd, &fid);
408                         if (rc != 0)
409                                 fprintf(stderr,
410                                         "llapi_path/fd2fid() on %d, rc=%lld\n",
411                                         fd, rc);
412                         else
413                                 printf(DFID"\n", PFID(&fid));
414                         fflush(stdout);
415                         break;
416                 case 'G':
417                         gid = atoi(commands + 1);
418                         if (ioctl(fd, LL_IOC_GROUP_LOCK, gid) == -1) {
419                                 save_errno = errno;
420                                 perror("ioctl(GROUP_LOCK)");
421                                 exit(save_errno);
422                         }
423                         break;
424                 case 'g':
425                         gid = atoi(commands + 1);
426                         if (ioctl(fd, LL_IOC_GROUP_UNLOCK, gid) == -1) {
427                                 save_errno = errno;
428                                 perror("ioctl(GROUP_UNLOCK)");
429                                 exit(save_errno);
430                         }
431                         break;
432                 case 'H':
433                         len = atoi(commands + 1);
434                         fd = llapi_file_open(fname, O_CREAT | O_WRONLY, 0644,
435                                              0, 0, len, LOV_PATTERN_RAID0 |
436                                              LOV_PATTERN_F_RELEASED);
437                         if (fd == -1) {
438                                 save_errno = errno;
439                                 perror("create stripe file");
440                                 exit(save_errno);
441                         }
442                         rc = fd;
443                         break;
444                 case 'j':
445                         if (flock(fd, LOCK_EX) == -1)
446                                 errx(-1, "flock()");
447                         break;
448                 case 'K':
449                         oldpath = POP_ARG();
450                         if (!oldpath)
451                                 oldpath = fname;
452
453                         if (link(oldpath, fname)) {
454                                 save_errno = errno;
455                                 perror("link()");
456                                 exit(save_errno);
457                         }
458                         break;
459                 case 'l':
460                         newfile = POP_ARG();
461                         if (!newfile)
462                                 newfile = fname;
463                         if (symlink(fname, newfile)) {
464                                 save_errno = errno;
465                                 perror("symlink()");
466                                 exit(save_errno);
467                         }
468                         break;
469                 case 'L':
470                         newfile = POP_ARG();
471                         if (!newfile)
472                                 newfile = fname;
473
474                         if (link(fname, newfile)) {
475                                 save_errno = errno;
476                                 perror("link()");
477                                 exit(save_errno);
478                         }
479                         break;
480                 case 'm':
481                         if (mknod(fname, S_IFREG | 0644, 0) == -1) {
482                                 save_errno = errno;
483                                 perror("mknod(S_IFREG|0644, 0)");
484                                 exit(save_errno);
485                         }
486                         break;
487                 case 'M':
488                         if (st.st_size == 0) {
489                                 fprintf(stderr,
490                                         "mmap without preceeding stat, or on zero length file.\n");
491                                 exit(-1);
492                         }
493                         mmap_len = st.st_size;
494                         mmap_ptr = mmap(NULL, mmap_len, PROT_WRITE | PROT_READ,
495                                         MAP_SHARED, fd, 0);
496                         if (mmap_ptr == MAP_FAILED) {
497                                 save_errno = errno;
498                                 perror("mmap");
499                                 exit(save_errno);
500                         }
501                         break;
502                 case 'n':
503                         oldpath = POP_ARG();
504                         if (!oldpath)
505                                 oldpath = fname;
506
507                         if (rename(oldpath, fname) < 0) {
508                                 save_errno = errno;
509                                 perror("rename()");
510                                 exit(save_errno);
511                         }
512                         break;
513                 case 'N':
514                         newfile = POP_ARG();
515                         if (!newfile)
516                                 newfile = fname;
517                         if (rename(fname, newfile)) {
518                                 save_errno = errno;
519                                 perror("rename()");
520                                 exit(save_errno);
521                         }
522                         break;
523                 case 'O':
524                         fd = open(fname, O_CREAT | O_RDWR, 0644);
525                         if (fd == -1) {
526                                 save_errno = errno;
527                                 perror("open(O_RDWR|O_CREAT)");
528                                 exit(save_errno);
529                         }
530                         rc = fd;
531                         break;
532                 case 'o':
533                         len = get_flags(commands + 1, &flags);
534                         commands += len;
535                         if (flags & O_CREAT)
536                                 fd = open(fname, flags, 0666);
537                         else
538                                 fd = open(fname, flags);
539                         if (fd == -1) {
540                                 save_errno = errno;
541                                 perror("open");
542                                 exit(save_errno);
543                         }
544                         rc = fd;
545                         break;
546                 case 'p':
547                         printf("%lld\n", last_rc);
548                         break;
549                 case 'r':
550                         len = atoi(commands + 1);
551                         if (len <= 0)
552                                 len = 1;
553                         if (bufsize < len) {
554                                 void *tmp;
555
556                                 tmp = realloc(buf, len + ALIGN_LEN);
557                                 if (!tmp) {
558                                         free(buf);
559                                         save_errno = errno;
560                                         perror("allocating buf for read\n");
561                                         exit(save_errno);
562                                 }
563                                 buf = tmp;
564                                 bufsize = len;
565                                 buf_align = (char *)((long)(buf + ALIGN_LEN) &
566                                                      ~ALIGN_LEN);
567                         }
568                         while (len > 0) {
569                                 rc = read(fd, buf_align, len);
570                                 if (rc == -1) {
571                                         save_errno = errno;
572                                         perror("read");
573                                         exit(save_errno);
574                                 }
575                                 if (rc < len) {
576                                         fprintf(stderr, "short read: %lld/%u\n",
577                                                 rc, len);
578                                         if (rc == 0)
579                                                 exit(ENODATA);
580                                 }
581                                 len -= rc;
582                                 if (verbose >= 2)
583                                         printf("%.*s\n", (int)rc, buf_align);
584                         }
585                         break;
586                 case 'R':
587                         for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
588                                 junk += mmap_ptr[i];
589                         break;
590                 case 's':
591                         if (stat(fname, &st) == -1) {
592                                 save_errno = errno;
593                                 perror("stat");
594                                 exit(save_errno);
595                         }
596                         break;
597                 case 'S':
598                         if (fstat(fd, &st) == -1) {
599                                 save_errno = errno;
600                                 perror("fstat");
601                                 exit(save_errno);
602                         }
603                         break;
604                 case 't':
605                         if (fchmod(fd, 0) == -1) {
606                                 save_errno = errno;
607                                 perror("fchmod");
608                                 exit(save_errno);
609                         }
610                         break;
611                 case 'T':
612                         len = atoi(commands + 1);
613                         if (ftruncate(fd, len) == -1) {
614                                 save_errno = errno;
615                                 printf("ftruncate (%d,%d)\n", fd, len);
616                                 perror("ftruncate");
617                                 exit(save_errno);
618                         }
619                         break;
620                 case 'u':
621                         if (unlink(fname) == -1) {
622                                 save_errno = errno;
623                                 perror("unlink");
624                                 exit(save_errno);
625                         }
626                         break;
627                 case 'U':
628                         if (munmap(mmap_ptr, mmap_len)) {
629                                 save_errno = errno;
630                                 perror("munmap");
631                                 exit(save_errno);
632                         }
633                         break;
634                 case 'v':
635                         verbose++;
636                         break;
637                 case 'V':
638                         len = get_flags(commands + 1, &flags);
639                         commands += len;
640                         len = -1; /* mdt index */
641                         if (commands[1] >= '0' && commands[1] <= '9')
642                                 len = atoi(commands + 1);
643                         fd = llapi_create_volatile_idx(fname, len, flags);
644                         if (fd < 0) {
645                                 perror("llapi_create_volatile");
646                                 exit(fd);
647                         }
648                         rc = fd;
649                         break;
650                 case 'w':
651                         len = atoi(commands + 1);
652                         if (len <= 0)
653                                 len = 1;
654                         if (bufsize < len) {
655                                 void *tmp;
656
657                                 tmp = realloc(buf, len + ALIGN_LEN);
658                                 if (!tmp) {
659                                         free(buf);
660                                         save_errno = errno;
661                                         perror("allocating buf for write\n");
662                                         exit(save_errno);
663                                 }
664                                 buf = tmp;
665                                 bufsize = len;
666                                 buf_align = (char *)((long)(buf + ALIGN_LEN) &
667                                                      ~ALIGN_LEN);
668                                 strncpy(buf_align, msg, bufsize);
669                         }
670                         while (len > 0) {
671                                 rc = write(fd, buf_align, len);
672                                 if (rc == -1) {
673                                         save_errno = errno;
674                                         perror("write");
675                                         exit(save_errno);
676                                 }
677                                 if (rc < len)
678                                         fprintf(stderr,
679                                                 "short write: %lld/%u\n",
680                                                 rc, len);
681                                 len -= rc;
682                         }
683                         break;
684                 case 'W':
685                         for (i = 0; i < mmap_len && mmap_ptr; i += 4096)
686                                 mmap_ptr[i] += junk++;
687                         break;
688                 case 'x': {
689                         __u64 dv;
690
691                         rc = llapi_get_data_version(fd, &dv, 0);
692                         if (rc) {
693                                 fprintf(stderr,
694                                         "cannot get file data version %lld\n",
695                                         rc);
696                                 exit(-rc);
697                         }
698                         printf("dataversion is %ju\n", (uintmax_t)dv);
699                         break;
700                 }
701                 case 'X': {
702                         __u32 layout_version;
703
704                         rc = llapi_get_ost_layout_version(fd, &layout_version);
705                         if (rc) {
706                                 fprintf(stderr,
707                                         "cannot get ost layout version %lld\n",
708                                         rc);
709                                 exit(-rc);
710                         }
711                         printf("ostlayoutversion: %u\n", layout_version);
712                         break;
713                 }
714                 case 'y':
715                         if (fsync(fd) == -1) {
716                                 save_errno = errno;
717                                 perror("fsync");
718                                 exit(save_errno);
719                         }
720                         break;
721                 case 'Y':
722                         if (fdatasync(fd) == -1) {
723                                 save_errno = errno;
724                                 perror("fdatasync");
725                                 exit(save_errno);
726                         }
727                         break;
728                 case 'z': {
729                         off_t off;
730
731                         len = atoi(commands + 1);
732                         off = lseek(fd, len, SEEK_SET);
733                         if (off == (off_t)-1) {
734                                 save_errno = errno;
735                                 perror("lseek");
736                                 exit(save_errno);
737                         }
738
739                         rc = off;
740                         break;
741                 }
742                 case 'Z': {
743                         off_t off;
744
745                         len = atoi(commands + 1);
746                         off = lseek(fd, len, SEEK_CUR);
747                         if (off == (off_t)-1) {
748                                 save_errno = errno;
749                                 perror("lseek");
750                                 exit(save_errno);
751                         }
752
753                         rc = off;
754                         break;
755                 }
756                 case '-':
757                 case '0':
758                 case '1':
759                 case '2':
760                 case '3':
761                 case '4':
762                 case '5':
763                 case '6':
764                 case '7':
765                 case '8':
766                 case '9':
767                         break;
768                 default:
769                         fprintf(stderr, "unknown command \"%c\"\n", *commands);
770                         fprintf(stderr, usage, argv[0]);
771                         exit(1);
772                 }
773         }
774
775         if (buf)
776                 free(buf);
777
778         return 0;
779 }